?前言?:
大家在學習完自定義型別結構體后,就可以嘗試自己寫一些小專案了,例如:通訊錄--圖書管理系統---學生資訊管理系統等專案,它們的內在邏輯都是相同的,這不僅可以提升大家的coding能力,還可以提升大家對于結構體的理解,
本博客以通訊錄的實作為例子
目錄
?前言?:
💎預期功能💎
💎具體功能的實作💎
一:如何存放聯系人的資訊
二:如何創建通訊錄
三:如何添加聯系人
四:如何列印出當前通訊錄
五:如何洗掉指定聯系人的資訊
💎總代碼的展示💎
💎預期功能💎
在做專案時,無論是大專案還是小專案,都要做到先理清:要實作專案的預期功能是什么----按照這些預期功能把專案進行拆解成一個個的功能----把每一個特定功能分裝成一個函式,這其實就是模塊化編程思想,在做專案時尤其重要,
🔑:預期通訊錄的功能如下
1:能存放聯系人的資訊(包括:姓名 年齡 性別 電話號碼 家庭住址 等資訊)
2:支持在這個通訊錄內部添加💎功能的具體實作💎聯系人的資訊
3:支持洗掉這個通訊錄中指定的聯系人的資訊
4:支持列印出整個通訊錄,顯示出整個通訊錄的資訊
5:支持查找指定聯系人,并列印出該聯系人的資訊
6:支持修改這個通訊錄中指定聯系人的資訊
💡:由上述預期通訊錄的功能可以將整個專案按功能拆成一個個模塊,再把一個個模塊分裝成一個個函式,
💎具體功能的實作💎
一:如何存放聯系人的資訊
💡:由于要描述每一個聯系人,我們需要記錄下每一位聯系人的姓名,年齡,性別,電話號碼,家庭住址,所以我們知道想要描述一個聯系人需要多種不同的資料型別,因此我們考慮用一個結構體來記錄聯系人的資訊,
🔑:具體代碼的實作:
struct PeoInfo
{
char name[NAME_MAX];//記錄聯系人的性別
int age;//聯系人的年齡
char sex[SEX_MAX];//聯系人的性別
char tele[TELE_MAX];//聯系人的電話號碼
char addr[ADDR_MAX];//聯系人的家庭地址
};
二:如何創建通訊錄
💡:首先我們要明確通訊錄中包含什么?
1:通訊錄中要有一塊空間來儲存聯系人的資訊,而聯系人的資訊我們明確了是結構體型別,
2:通訊錄中要有一個變數記錄當前通訊錄中聯系人的個數
3:通訊錄中要有一個變數來記錄當前通訊錄的總容量
🔑:讀到這里,大家可能意識到了,創建一個通訊錄遇到的最大困難是:如何為通訊錄分配空間?
如果我們預先確定其最大容量,即寫一個總容量大小確定的通訊錄(例如創建一個最多放1000個聯系人的通訊錄),這時就存在問題,由于空間在運行前就固定好了,所以你無論開辟多大空間都不太合適,預先開辟的空間大了,就會浪費空間;預先開辟空間小了,就會導致放不下聯系人,
在這里我們就可以引入動態記憶體開辟的思路,我們可以預先開辟一個較小空間,如果在放入資料時,發現通訊錄滿了,就給通訊錄擴容,這樣可以很好的減少空間的浪費,
💡:在這里還要解決一個問題,那就是什么時候給通訊錄進行擴容處理,很容易就能夠分析出來,只有在準備添加聯系人時,發現通訊錄已經滿了,此時才需要擴容,也就是說擴容發生在添加聯系人的時候,
//由于通訊錄中包含多種不同的資料型別
//因此通訊錄應該是結構體型別
//動態容量版本
struct Contact
{
struct PeoInfo* date;//指向n個聯系人空間的起始地址 作用是維護整個整個通訊錄
int sz;//通訊錄有效元素的個數
int capcity;//當前通訊錄的最大容量
};
//動態容量版本
//DEFAULT_SZ是定義的宏常量 初始值預開辟的較小容量是3
void InitContact(struct Contact* pc)
{
//給通訊錄初始化較小的空間
pc->sz = 0;
pc->date=(struct PeoInfo*)malloc(DEFAULT_SZ *sizeof(struct PeoInfo));
pc->capcity = DEFAULT_SZ;
}
void AddContact(struct Contact* pc)
{
if (pc->sz == pc->capcity)
{
//需要進行擴容操作
struct PeoInfo* tmp=(struct PeoInfo*)realloc(pc->date, (DEFAULT_SZ + 2) * sizeof(struct PeoInfo));
if (tmp != NULL)
{
pc->date = tmp;
pc->capcity += 2;
printf("擴容成功\n");
}
else {
printf("擴容失敗,通訊錄已滿,無法添加聯系人\n");
return 0;
}
}
//在動態增長的版本,不存在滿的概念
//只要空間滿就會擴容,不存在添加失敗的情況
//if (pc->sz == MAX)
//{
// printf("添加失敗,通訊錄已滿了\n");
//}
printf("請輸入姓名:>");
scanf("%s", pc->date[pc->sz].name);
printf("請輸入年齡:>");
scanf("%d",&(pc->date[pc->sz].age));
printf("請輸入性別:>");
scanf("%s", pc->date[pc->sz].sex);
printf("請輸入電話:>");
scanf("%s", pc->date[pc->sz].tele);
printf("請輸入地址:>");
scanf("%s", pc->date[pc->sz].addr);
pc->sz++;
printf("添加成功\n");
}
其中的代碼塊是從總代碼中截取出來的,有一些函式,變數未曾宣告定義,
三:如何添加聯系人
💡:想要在通訊錄中添加聯系人十分簡單,由于我們的通訊錄總容量是動態開辟的,所以在擴容成功的前提下不存在通訊錄已經滿了,無法添加的情況,我們只需要把人的資訊一個個輸入就夠了,
🔑:代碼展示
void AddContact(struct Contact* pc)
{
if (pc->sz == pc->capcity)
{
//需要進行擴容操作
struct PeoInfo* tmp=(struct PeoInfo*)realloc(pc->date, (DEFAULT_SZ + 2) * sizeof(struct PeoInfo));
if (tmp != NULL)
{
pc->date = tmp;
pc->capcity += 2;
printf("擴容成功\n");
}
else {
printf("擴容失敗,通訊錄已滿,無法添加聯系人\n");
return 0;
}
}
//在動態增長的版本,不存在滿的概念
//只要空間滿就會擴容,不存在添加失敗的情況
//if (pc->sz == MAX)
//{
// printf("添加失敗,通訊錄已滿了\n");
//}
printf("請輸入姓名:>");
scanf("%s", pc->date[pc->sz].name);
printf("請輸入年齡:>");
scanf("%d",&(pc->date[pc->sz].age));
printf("請輸入性別:>");
scanf("%s", pc->date[pc->sz].sex);
printf("請輸入電話:>");
scanf("%s", pc->date[pc->sz].tele);
printf("請輸入地址:>");
scanf("%s", pc->date[pc->sz].addr);
pc->sz++;
printf("添加成功\n");
}
四:如何列印出當前通訊錄
首先我們先來看一看預期達到的效果,再按照這個預期的效果來寫代碼

從這個表格可以看出我們預期實作的效果,類似一個表格,上面一行寫標簽,下面寫具體聯系人的資訊,
想要達到這個效果,無疑我們必須要會靈活的運用格式化輸出,如果不會的可以看看書本,在此不做過多的贅述,
🔑:代碼展示:
void ShowContact(struct Contact* pc)
{
int i = 0;
//列印表格標簽名 注意格式化輸出
printf("%20s\t%5s\t%8s\t%20s\t%30s\n", "name", "age", "sex", "tele", "addr");
for (i = 0; i < pc->sz; i++)
{
//利用回圈逐個列印聯系人的資訊
printf("%20s\t%5d\t%8s\t%20s\t%30s\n", pc->date[i].name,
pc->date[i].age,
pc->date[i].sex,
pc->date[i].tele,
pc->date[i].addr);
}
}
五:如何洗掉指定聯系人的資訊
💡:我們想洗掉指定聯系人的資訊,前提條件是我們要通過聯系人的姓名在通訊錄中找到該聯系人,所以這個問題的難點就在于以下兩點:
1:通過姓名找到該聯系人后,要回傳什么資訊,執行什么樣的操作?
2:如何在date指標所維護的聯系人資訊空間中,洗掉指定的元素?
🔑:現在我們來解決以上的兩個問題
1:在找到時,我們希望能回傳該聯系人的下標(即把動態變化的空間視為一個陣列),若找不到則回傳-1
2:要在這個陣列中洗掉一個聯系人其實很簡單,我們只需要把該聯系人之后的所有聯系人整體向左移動一個單位,然后讓有效聯系人個數減一就夠了
💡:代碼展示
//對于按名字查找指定聯系人時我們定義如下規則
//如果查找到了,則回傳date陣列中該人資訊的下標
//如果找不到,則回傳-1
int SearchByNameContact(const char name[], struct Contact* pc)
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(name, pc->date[i].name) == 0)
{
return i;
}
}
return -1;
}
//洗掉指定聯系人
void DelContact(struct Contact* pc)
{
if (pc->sz == 0)
{
printf("該通訊錄為空,無法洗掉\n");
return;
}
printf("請輸入你要洗掉聯系人的姓名:>");
char name[NAME_MAX] = { 0 };
scanf("%s", name);
int pos = SearchByNameContact(name,pc);
if (pos == -1)
{
printf("你想洗掉的聯系人不存在\n");
}
else
{
//對聯系人進行洗掉
for (int i = pos; i < pc->sz - 1; i++)
{
pc->date[i] = pc->date[i + 1];
}
pc->sz--;
printf("洗掉成功\n");
}
}
而查找功能其實就是按名字查找函式和列印的組合,
而修改功能其實就是按名字查找和重新錄入資訊的組合,
這兩種功能的思路和以上方法思路重合,在此就不做過多的贅述,
💎總代碼的展示💎
有于該專案涉及到很多的函式,因此我們分模塊來寫,在contact.h中宣告函式 ,在contact.c中給出函式的具體定義 在text.c中使用函式并列印選單
💡;contact.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#define NAME_MAX 20
#define SEX_MAX 5
#define TELE_MAX 20
#define ADDR_MAX 30
#define MAX 1000
#define DEFAULT_SZ 3
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct PeoInfo
{
char name[NAME_MAX];
int age;
char sex[SEX_MAX];
char tele[TELE_MAX];
char addr[ADDR_MAX];
};
//靜態版本
//struct Contact
//{
將1000個人的資料放在date陣列中
// struct PeoInfo date[MAX];
// int sz;//記錄當前通訊錄有效學生資訊的個數
//};
//動態版本
struct Contact
{
struct PeoInfo* date;
int sz;//通訊錄有效元素的個數
int capcity;//當前通訊錄的最大容量
};
//初始化通訊錄
void InitContact(struct Contact* pc);
//添加聯系人
void AddContact(struct Contact* pc);
//顯示通訊錄(列印通訊錄)
void ShowContact(struct Contact* pc);
//查找練習人,并輸出
void SearchContact(const struct Contact* pc);
//洗掉指定的聯系人
void DelContact(struct Contact* pc);
//修改指定的聯系人
void ModifyContact(struct Contact* pc);
💡:text.c的代碼
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include"contact.h"
//寫一個通訊錄頭檔案
void menu()
{
printf("***********************************\n");
printf("***** 1.add 2.del *************\n");
printf("*****3.search 4.modify *********\n");
printf("***** 5.show 6.sort **********\n");
printf("****** 0.exit ********\n");
printf("***********************************\n");
}
enum option
{
EXIT,
ADD,
DEL,
SEARCH,
MODIFY,
SHOW,
SORT
};
int main()
{
//1:創建一個通訊錄
struct Contact con;
//2:對通訊錄進行初始化
InitContact(&con);
int input = 0;
do
{
menu();
printf("請選擇:>");
scanf("%d", &input);
switch (input)
{
case ADD:
AddContact(&con);
break;
case DEL:
DelContact(&con);
break;
case SHOW:
ShowContact(&con);
break;
case EXIT:
printf("退出通訊錄\n");
break;
case SEARCH:
SearchContact(&con);
break;
case MODIFY:
ModifyContact(&con);
default:
printf("選擇錯誤,請重新選擇\n");
break;
}
} while (input);
return 0;
}
💡:contact.c的代碼
#define _CRT_SECURE_NO_WARNINGS 1
#include"contact.h"
//OK 已經實作功靜態容量的通訊錄
//將靜態容量的通訊錄-->動態容量的通訊錄 升級版本
//void InitContact(struct Contact* pc)
//{
// pc->sz = 0;
// memset(pc->date,0,sizeof(pc->date));
//}
//動態容量版本
void InitContact(struct Contact* pc)
{
//給通訊錄初始化較小的空間
pc->sz = 0;
pc->date=(struct PeoInfo*)malloc(DEFAULT_SZ *sizeof(struct PeoInfo));
pc->capcity = DEFAULT_SZ;
}
void AddContact(struct Contact* pc)
{
if (pc->sz == pc->capcity)
{
//需要進行擴容操作
struct PeoInfo* tmp=(struct PeoInfo*)realloc(pc->date, (DEFAULT_SZ + 2) * sizeof(struct PeoInfo));
if (tmp != NULL)
{
pc->date = tmp;
pc->capcity += 2;
printf("擴容成功\n");
}
else {
printf("擴容失敗,通訊錄已滿,無法添加聯系人\n");
return 0;
}
}
//在動態增長的版本,不存在滿的概念
//只要空間滿就會擴容,不存在添加失敗的情況
//if (pc->sz == MAX)
//{
// printf("添加失敗,通訊錄已滿了\n");
//}
printf("請輸入姓名:>");
scanf("%s", pc->date[pc->sz].name);
printf("請輸入年齡:>");
scanf("%d",&(pc->date[pc->sz].age));
printf("請輸入性別:>");
scanf("%s", pc->date[pc->sz].sex);
printf("請輸入電話:>");
scanf("%s", pc->date[pc->sz].tele);
printf("請輸入地址:>");
scanf("%s", pc->date[pc->sz].addr);
pc->sz++;
printf("添加成功\n");
}
void ShowContact(struct Contact* pc)
{
int i = 0;
printf("%20s\t%5s\t%8s\t%20s\t%30s\n", "name", "age", "sex", "tele", "addr");
for (i = 0; i < pc->sz; i++)
{
//進行通訊表的列印
printf("%20s\t%5d\t%8s\t%20s\t%30s\n", pc->date[i].name,
pc->date[i].age,
pc->date[i].sex,
pc->date[i].tele,
pc->date[i].addr);
}
}
//靜態版通訊錄--->升級到動態版
//
//如果在通訊錄中找到了聯系人,則列印出該聯系人的資訊
//如果沒有該聯系人則回傳false
//并不是像hash 查找一樣 需要一個個的查找
//對于按名字查找指定聯系人時我們定義如下規則
//如果查找到了,則回傳date陣列中該人資訊的下標
//如果找不到,則回傳-1
int SearchByNameContact(const char name[], struct Contact* pc)
{
int i = 0;
for (i = 0; i < pc->sz; i++)
{
if (strcmp(name, pc->date[i].name) == 0)
{
return i;
}
}
return -1;
}
//洗掉指定聯系人
void DelContact(struct Contact* pc)
{
if (pc->sz == 0)
{
printf("該通訊錄為空,無法洗掉\n");
return;
}
printf("請輸入你要洗掉聯系人的姓名:>");
char name[NAME_MAX] = { 0 };
scanf("%s", name);
int pos = SearchByNameContact(name,pc);
if (pos == -1)
{
printf("你想洗掉的聯系人不存在\n");
}
else
{
//對聯系人進行洗掉
for (int i = pos; i < pc->sz - 1; i++)
{
pc->date[i] = pc->date[i + 1];
}
pc->sz--;
printf("洗掉成功\n");
}
}
void SearchContact(const struct Contact* pc)
{
printf("請輸入你要查找聯系人的名字:>");
char name[NAME_MAX] = { 0 };
scanf("%s", name);
int ret = SearchByNameContact(name,pc);
if (ret == -1)
{
printf("查無此人\n");
return 0;
}
printf("%20s\t%5s\t%8s\t%20s\t%30s\n", "name", "age", "sex", "tele", "addr");
printf("%20s\t%5d\t%8s\t%20s\t%30s\n", pc->date[ret].name,
pc->date[ret].age,
pc->date[ret].sex,
pc->date[ret].tele,
pc->date[ret].addr);
}
void ModifyContact(struct Contact* pc)
{
printf("請輸入你要修改的聯系人\n");
char name[NAME_MAX] = { 0 };
scanf("%s", name);
int pos = SearchByNameContact(name,pc);
if (pos == -1)
{
printf("要修改的聯系人不存在\n");
return;
}
else
{
printf("請輸入姓名:>");
scanf("%s", pc->date[pos].name);
printf("請輸入年齡:>");
scanf("%d", &(pc->date[pos].age));
printf("請輸入性別:>");
scanf("%s", pc->date[pos].sex);
printf("請輸入電話:>");
scanf("%s", pc->date[pos].tele);
printf("請輸入地址:>");
scanf("%s", pc->date[pos].addr);
printf("修改成功\n");
}
}
以上代碼,還可做優化在此僅作參考,若有更好的演算法,還望能夠私信告知,多謝各位,
由于本人水平十分有限,若有錯誤請即使告知!如果有幫助別忘了,萬分感謝,
點贊👍 收藏? 關注?
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/302832.html
標籤:其他
