基于C語言打造高效通訊錄的示例代碼
本篇博客會講解如何使用C語言實現(xiàn)一個通訊錄。實現(xiàn)通訊錄的過程中,會大量用到C語言的知識點(diǎn),包括但不限于:函數(shù)、自定義類型、指針、動態(tài)內(nèi)存管理、文件操作,這些知識點(diǎn)在我的其他博客中都有講解過,歡迎大家閱讀,這里就不進(jìn)行系統(tǒng)的復(fù)習(xí)了。
先來梳理下需求:
1.通訊錄能夠存儲的聯(lián)系人的信息有:姓名、年齡、性別、電話、住址。
2.這個通訊錄不能是“靜態(tài)的”,而應(yīng)該是“動態(tài)的”,也就是說,需要用到動態(tài)內(nèi)存管理的知識。這是因為,靜態(tài)的通訊錄的容量是固定的,空間太大可能浪費(fèi),太小了又不夠存。
3.由于當(dāng)程序開始運(yùn)行后,通訊錄的數(shù)據(jù)是存儲在內(nèi)存中的,一旦程序運(yùn)行結(jié)束,執(zhí)行完main函數(shù)的return 0;后,空間就被操作系統(tǒng)回收了,相當(dāng)于數(shù)據(jù)就丟了。為了能夠?qū)崿F(xiàn)“永久保存”的效果,我們要在程序退出前,把數(shù)據(jù)保存到文件中,這又涉及到文件操作的相關(guān)知識點(diǎn)。
4.類似順序表這種數(shù)據(jù)結(jié)構(gòu)的基本操作,通訊錄要能做到:增刪查改+排序+打印,即增加聯(lián)系人、刪除聯(lián)系人、查找聯(lián)系人、修改聯(lián)系人、排序聯(lián)系人、打印聯(lián)系人等等。
下面我們開始吧!
準(zhǔn)備工作
以下是菜單里的一些選項,聲明成枚舉類型是比較合適的。
// 菜單里的不同選項 enum Option { EXIT, // 退出 ADD, // 增加聯(lián)系人 DEL, // 刪除聯(lián)系人 SEARCH, // 查找聯(lián)系人 MODIFY, // 修改聯(lián)系人 SHOW, // 顯示聯(lián)系人 SORT // 排序 };
由于聯(lián)系人的姓名、性別、電話和住址都是字符串,要存儲在字符數(shù)組中,最好先聲明它們的容量。
// 各信息的存儲容量 #define MAX_NAME 20 // 名字 #define MAX_SEX 5 // 性別 #define MAX_TELE 12 // 電話 #define MAX_ADDR 30 // 住址
我們后面在進(jìn)行動態(tài)內(nèi)存管理時,需要知道初始的容量和每次擴(kuò)容的容量,也聲明一下:
// 動態(tài)內(nèi)存默認(rèn)存儲的數(shù)據(jù) #define DEFAULT_SZ 3 // 若不夠存,每次擴(kuò)容的數(shù)量 #define INC_SZ 2
再聲明一個結(jié)構(gòu)體,表示一個人的信息,包括姓名、年齡、性別、電話、住址。
// 表示一個人的信息 typedef struct PeoInfo { char name[MAX_NAME]; // 姓名 int age; // 年齡 char sex[MAX_SEX]; // 性別 char tele[MAX_TELE]; // 電話 char addr[MAX_ADDR]; // 住址 }PeoInfo;
類似數(shù)據(jù)結(jié)構(gòu)中的“順序表”的結(jié)構(gòu),定義一個結(jié)構(gòu)體,用于存儲通訊錄中的信息,包括一個動態(tài)開辟的數(shù)組,數(shù)組中有效數(shù)據(jù)的個數(shù),以及數(shù)組當(dāng)前動態(tài)開辟的容量。
// 通訊錄 typedef struct Contact { PeoInfo* data; // data指向了存放數(shù)據(jù)的空間 int sz; // 記錄通訊錄中的有效信息個數(shù) int capacity; // 通訊錄當(dāng)前的容量 }Contact;
下面我們開始實現(xiàn)程序的主體邏輯。先從主函數(shù)寫起,把主要的功能都封裝成函數(shù):
// 打印菜單 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"); } int main() { int input = 0; // 存儲用戶輸入的數(shù)據(jù) Contact con; // 通訊錄 // 初始化通訊錄 // 加載文件的信息到通訊錄中 InitContact(&con); do { menu(); // 菜單 printf("請選擇:>"); scanf("%d", &input); switch (input) { case ADD: // 添加聯(lián)系人 AddContact(&con); break; case DEL: // 刪除聯(lián)系人 DelContact(&con); break; case SEARCH: // 查找指定聯(lián)系人 SearchContact(&con); break; case MODIFY: // 修改指定聯(lián)系人的信息 ModifyContact(&con); break; case SHOW: // 展示聯(lián)系人信息 ShowContact(&con); break; case SORT: // 排序 SortContact(&con); break; case EXIT: // 退出通訊錄 // 保存通訊錄到文件中 SaveContact(&con); // 銷毀通訊錄 DestroyConact(&con); printf("退出通訊錄\n"); break; default: printf("選擇錯誤\n"); break; } } while (input); return 0; }
初始化通訊錄
先定義一個函數(shù)InitContact,它的作用是初始化通訊錄。函數(shù)的參數(shù)是一個指向Contact結(jié)構(gòu)體的指針。函數(shù)的具體實現(xiàn)如下:
- 函數(shù)的第一行使用assert宏檢查指針是否有效,如果無效則程序會中止運(yùn)行。
- 接下來,函數(shù)使用malloc函數(shù)開辟了一塊內(nèi)存空間,用于存儲PeoInfo結(jié)構(gòu)體數(shù)組。這個數(shù)組的大小是DEFAULT_SZ,即默認(rèn)容量。如果開辟空間失敗,則會輸出錯誤信息并返回。
- 如果開辟空間成功,則將通訊錄的大小sz和容量capacity都設(shè)置為DEFAULT_SZ,并調(diào)用LoadContact函數(shù)將文件中的信息加載到通訊錄中。
void InitContact(Contact* pc) { // 檢查指針有效性 assert(pc); // 先開辟默認(rèn)的容量 pc->data = (PeoInfo*)malloc(DEFAULT_SZ * sizeof(PeoInfo)); // 檢查開辟空間是否成功 if (pc->data == NULL) { // 開辟空間失敗 printf("通訊錄初始化失敗:%s\n", strerror(errno)); return; } // 開辟空間成功 pc->sz = 0; pc->capacity = DEFAULT_SZ; //加載文件的信息到通訊錄 LoadContact(pc); }
從文件中加載信息
再定義一個函數(shù)LoadContact,用于從文件中讀取聯(lián)系人信息并存儲到內(nèi)存中。函數(shù)的參數(shù)是一個指向Contact結(jié)構(gòu)體的指針,表示要將讀取到的聯(lián)系人信息存儲到哪個數(shù)據(jù)結(jié)構(gòu)中。函數(shù)的具體實現(xiàn)如下:
- 首先使用assert函數(shù)檢查傳入的指針是否有效,如果無效則直接返回。
- 然后使用fopen函數(shù)打開名為"contact.dat"的二進(jìn)制文件,如果打開失敗則說明可能是第一次運(yùn)行通訊錄,沒有數(shù)據(jù)文件,直接返回。
- 接著使用一個while循環(huán),每次讀取一個PeoInfo結(jié)構(gòu)體大小的數(shù)據(jù),即一個聯(lián)系人的信息,存儲到臨時變量tmp中。
- 調(diào)用CheckCapacity函數(shù)檢查當(dāng)前動態(tài)數(shù)組的容量是否足夠存儲讀取到的聯(lián)系人信息,如果不夠則進(jìn)行擴(kuò)容。
- 將讀取到的聯(lián)系人信息存儲到動態(tài)數(shù)組中,即將tmp變量中的數(shù)據(jù)存儲到data數(shù)組的末尾,并將sz變量加一。
- 循環(huán)結(jié)束后,關(guān)閉文件并將文件指針置為 NULL。
void LoadContact(Contact* pc) { // 檢查指針有效性 assert(pc); // 打開文件 FILE* pf = fopen("contact.dat", "rb"); // 檢查打開文件是否成功 if (pf == NULL) { // 打開文件失敗,可能是第一次運(yùn)行通訊錄,并沒有數(shù)據(jù)文件 //perror("LoadContact::fopen"); return; } // 讀文件 PeoInfo tmp = { 0 }; // 存儲讀取到的數(shù)據(jù) // 每次讀一個數(shù)據(jù) while (fread(&tmp, sizeof(PeoInfo), 1, pf)) { // 檢查容量,不夠的話要擴(kuò)容 CheckCapacity(pc); // 存儲從文件讀取到的數(shù)據(jù) pc->data[pc->sz] = tmp; pc->sz++; } // 關(guān)閉文件 fclose(pf); pf = NULL; }
檢查容量
再定義一個函數(shù)CheckCapacity,用于檢查并擴(kuò)容動態(tài)數(shù)組。函數(shù)的參數(shù)是一個指向Contact結(jié)構(gòu)體的指針。函數(shù)的具體實現(xiàn)如下:
- 函數(shù)首先使用assert宏檢查傳入的指針是否有效。然后,它檢查當(dāng)前數(shù)組是否需要擴(kuò)容。如果數(shù)組的大小已經(jīng)等于容量,就需要擴(kuò)容。
- 在需要擴(kuò)容的情況下,函數(shù)使用realloc函數(shù)重新分配內(nèi)存。realloc函數(shù)會嘗試將原來分配的內(nèi)存塊擴(kuò)大到指定的大小。如果擴(kuò)容成功,realloc函數(shù)會返回一個指向新內(nèi)存塊的指針,否則返回NULL。
- 如果realloc函數(shù)返回NULL,說明擴(kuò)容失敗,函數(shù)會輸出錯誤信息并返回0。如果realloc函數(shù)返回非空指針,說明擴(kuò)容成功,函數(shù)會更新數(shù)組的起始位置和容量,并輸出擴(kuò)容成功的信息。最后,函數(shù)返回1,表示擴(kuò)容成功或者不需要擴(kuò)容。
// 擴(kuò)容失敗,返回0 // 擴(kuò)容成功,不需要擴(kuò)容,返回1 static int CheckCapacity(Contact* pc) { // 檢查指針有效性 assert(pc); // 檢查是否需要擴(kuò)容 if (pc->sz == pc->capacity) { // 需要擴(kuò)容 PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo)); // 檢查是否擴(kuò)容成功 if (ptr == NULL) { // 擴(kuò)容失敗 printf("CheckCapacity:%s\n", strerror(errno)); return 0; } else { // 擴(kuò)容成功 // 更新數(shù)組的起始位置 pc->data = ptr; // 更新容量 pc->capacity += INC_SZ; printf("增容成功,當(dāng)前容量:%d\n", pc->capacity); } } return 1; }
銷毀通訊錄
再定義一個函數(shù)DestroyContact,用來銷毀通訊錄。函數(shù)的參數(shù)是一個指向Contact結(jié)構(gòu)體的指針。函數(shù)的具體實現(xiàn)如下:
- 先使用assert宏檢查指針的有效性。
- 釋放動態(tài)數(shù)組占用的內(nèi)存空間。
- 把結(jié)構(gòu)體的變量置空。
void DestroyConact(Contact* pc) { // 檢查指針有效性 assert(pc); // 釋放內(nèi)存空間 free(pc->data); pc->data = NULL; pc->capacity = 0; pc->sz = 0; printf("釋放內(nèi)存.....\n"); }
添加聯(lián)系人
接著定義一個函數(shù)AddContact,參數(shù)仍然是一個指向Contact結(jié)構(gòu)體的指針,實現(xiàn)向通訊錄中添加聯(lián)系人的功能。具體實現(xiàn)如下:
- 首先檢查傳入的指針是否有效,如果無效則使用assert宏觸發(fā)斷言,程序終止。
- 調(diào)用CheckCapacity函數(shù)檢查通訊錄是否需要擴(kuò)容,如果需要則進(jìn)行擴(kuò)容操作。如果擴(kuò)容失敗,則輸出提示信息并返回。
- 如果擴(kuò)容成功,則提示用戶輸入聯(lián)系人信息,包括名字、年齡、性別、電話和地址。這些信息將被存儲在通訊錄的data數(shù)組中,下標(biāo)為 sz。
- 最后更新有效數(shù)據(jù)個數(shù)sz,并輸出添加成功的提示信息。
void AddContact(Contact* pc) { // 檢查指針有效性 assert(pc); // 擴(kuò)容,同時檢查是否成功 if (0 == CheckCapacity(pc)) { // 擴(kuò)容失敗 printf("空間不夠,擴(kuò)容失敗\n"); return; } else { // 輸入聯(lián)系人信息 printf("請輸入名字:>"); scanf("%s", pc->data[pc->sz].name); printf("請輸入年齡:>"); scanf("%d", &(pc->data[pc->sz].age)); printf("請輸入性別:>"); scanf("%s", pc->data[pc->sz].sex); printf("請輸入電話:>"); scanf("%s", pc->data[pc->sz].tele); printf("請輸入地址:>"); scanf("%s", pc->data[pc->sz].addr); // 更新有效數(shù)據(jù)個數(shù) pc->sz++; printf("添加成功\n"); } }
打印數(shù)據(jù)
接下來定義一個函數(shù) ShowContact,用于打印聯(lián)系人信息。函數(shù)接受一個指向Contact結(jié)構(gòu)體的指針pc。具體實現(xiàn)如下:
- 函數(shù)首先使用assert宏檢查指針pc是否有效,如果無效則程序會崩潰并輸出錯誤信息。
- 接下來,函數(shù)使用printf函數(shù)打印聯(lián)系人信息。首先打印表頭,包括姓名、年齡、性別、電話和地址。然后使用循環(huán)遍歷pc中的每個聯(lián)系人,打印其姓名、年齡、性別、電話和地址。
- 在打印時使用格式化字符串,其中 %s 表示字符串,%-10s 表示左對齊并占用 10 個字符的字符串,%-4d 表示左對齊并占用 4 個字符的整數(shù),%-5s 表示左對齊并占用 5 個字符的字符串,%-12s 表示左對齊并占用 12 個字符的字符串,%-30s 表示左對齊并占用 30 個字符的字符串。
void ShowContact(const Contact* pc) { // 檢查指針有效性 assert(pc); // 打印效果 // 姓名 年齡 性別 電話 地址 // zhangsan 20 男 123456 北京 // // 打印標(biāo)題 printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年齡", "性別", "電話", "地址"); // 打印數(shù)據(jù) for (int i = 0; i < pc->sz; ++i) { printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr); } }
刪除聯(lián)系人
接下來實現(xiàn)通訊錄的刪除聯(lián)系人功能。具體實現(xiàn)如下:
FindByName函數(shù):根據(jù)指定名字,在通訊錄中查找聯(lián)系人信息。函數(shù)參數(shù)為指向Contact結(jié)構(gòu)體的指針和要查找的名字。函數(shù)返回值為查找到的聯(lián)系人在通訊錄中的下標(biāo),如果沒找到則返回-1。
DelContact函數(shù):刪除通訊錄中指定聯(lián)系人。函數(shù)參數(shù)為指向Contact結(jié)構(gòu)體的指針。函數(shù)實現(xiàn)如下:
- 檢查指針有效性,如果為空則直接返回。
- 檢查通訊錄中是否還有數(shù)據(jù),如果沒有則輸出提示信息并返回。
- 獲取用戶輸入的要刪除的聯(lián)系人名字。
- 調(diào)用FindByName函數(shù)查找要刪除的聯(lián)系人在通訊錄中的下標(biāo)。
- 如果沒找到,則輸出提示信息并返回。
- 如果找到了,則使用memmove函數(shù)將該聯(lián)系人后面的所有聯(lián)系人向前移動一個位置,覆蓋掉要刪除的聯(lián)系人。
- 更新通訊錄中的有效數(shù)據(jù)個數(shù)。
- 輸出刪除成功的提示信息。
// 根據(jù)指定名字,查找聯(lián)系人信息 static int FindByName(const Contact* pc, char name[]) { // 檢查指針有效性 assert(pc); assert(name); // 遍歷數(shù)組 for (int i = 0; i < pc->sz; ++i) { // 檢查名字是否匹配 if (0 == strcmp(pc->data[i].name, name)) { // 找到了 return i; } } // 沒找到 return -1; } void DelContact(Contact* pc) { // 檢查指針有效性 assert(pc); // 檢查是否還有數(shù)據(jù) if (pc->sz == 0) { // 通訊錄已空 printf("通訊錄為空,無法刪除\n"); return; } char name[MAX_NAME] = { 0 }; // 存儲用戶輸入的數(shù)據(jù) // 用戶輸入信息 printf("輸入要刪除人的名字:>"); scanf("%s", name); // 找到要刪除的人的下標(biāo) int pos = FindByName(pc, name); // 檢查是否找到 if (pos == -1) { // 沒找到 printf("要刪除的人不存在\n"); return; } // 刪除pos位置上的數(shù)據(jù) // 挪動pos后面的數(shù)據(jù),覆蓋pos memmove(pc->data + pos, pc->data + pos + 1, sizeof(PeoInfo) * (pc->sz - pos - 1)); // 更新有效數(shù)據(jù)個數(shù) pc->sz--; printf("刪除成功\n"); }
查找聯(lián)系人
接著實現(xiàn)在通訊錄中根據(jù)姓名查找聯(lián)系人的功能。具體實現(xiàn)如下:
- 函數(shù)名為SearchContact,接受一個指向Contact結(jié)構(gòu)體的指針pc作為參數(shù)。
- 第一行代碼使用assert宏檢查指針pc是否有效,如果pc為 NULL,則程序會崩潰并輸出錯誤信息。
- 定義一個char類型的數(shù)組name,長度為MAX_NAME,用于存儲用戶輸入的要查找的人的名字。
- 使用printf函數(shù)輸出提示信息,讓用戶輸入要查找的人的名字。
- 使用scanf函數(shù)讀取用戶輸入的名字,存儲到name數(shù)組中。
- 調(diào)用FindByName函數(shù),在通訊錄中查找名字為name的聯(lián)系人,返回值為該聯(lián)系人在通訊錄中的位置,如果沒找到則返回-1。
- 判斷FindByName函數(shù)的返回值,如果為-1,則說明沒有找到要查找的聯(lián)系人,使用printf函數(shù)輸出提示信息,并直接返回。
- 如果FindByName函數(shù)的返回值不為-1,則說明找到了要查找的聯(lián)系人,使用printf函數(shù)輸出通訊錄的標(biāo)題行,包括姓名、年齡、性別、電話、地址等信息。
- 使用printf函數(shù)輸出找到的聯(lián)系人的具體信息,包括姓名、年齡、性別、電話、地址等信息,這些信息都存儲在Contact結(jié)構(gòu)體中的data數(shù)組中,通過pc指針訪問。pos變量表示要查找的聯(lián)系人在data數(shù)組中的位置。注意,這里使用了%-10s、%-4d等格式控制符,表示輸出字符串時左對齊,并且占用固定的寬度,方便對齊。
void SearchContact(const Contact* pc) { // 檢查指針有效性 assert(pc); char name[MAX_NAME] = { 0 }; // 存儲用戶輸入的信息 // 用戶輸入數(shù)據(jù) printf("請輸入要查找人的名字:>"); scanf("%s", name); // 查找 int pos = FindByName(pc, name); // 檢查是否找到 if (pos == -1) { // 沒找到 printf("要查找的人不存在\n"); return; } // 打印標(biāo)題行 printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年齡", "性別", "電話", "地址"); // 打印數(shù)據(jù) printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); }
修改聯(lián)系人
再下來實現(xiàn)一個函數(shù),函數(shù)的參數(shù)仍然是一個指向通訊錄結(jié)構(gòu)體類型Contact的指針,用于修改通訊錄中的聯(lián)系人信息。具體實現(xiàn)如下:
- 使用assert宏函數(shù)檢查指針有效性,如果指針為空,則程序會終止。
- 定義一個char類型數(shù)組name,用于存儲用戶輸入的聯(lián)系人名字。
- 使用printf函數(shù)提示用戶輸入要修改的聯(lián)系人名字,并使用scanf函數(shù)讀取用戶輸入的名字。
- 調(diào)用FindByName函數(shù)查找通訊錄中是否存在該聯(lián)系人,如果不存在則輸出提示信息并返回。
- 如果存在該聯(lián)系人,則使用scanf函數(shù)分別讀取用戶輸入的聯(lián)系人信息,包括名字、年齡、性別、電話和地址,并將這些信息存儲到通訊錄結(jié)構(gòu)體中對應(yīng)的位置。
- 最后使用printf函數(shù)輸出修改成功的提示信息。
void ModifyContact(Contact* pc) { // 檢查指針有效性 assert(pc); char name[MAX_NAME] = { 0 }; // 存儲用戶輸入的信息 // 用戶輸入數(shù)據(jù) printf("請輸入要修改人的名字:>"); scanf("%s", name); // 查找 int pos = FindByName(pc, name); // 檢查是否找到 if (pos == -1) { // 沒找到 printf("要修改的人不存在\n"); return; } // 修改 // 用戶輸入信息 printf("請輸入名字:>"); scanf("%s", pc->data[pos].name); printf("請輸入年齡:>"); scanf("%d", &(pc->data[pos].age)); printf("請輸入性別:>"); scanf("%s", pc->data[pos].sex); printf("請輸入電話:>"); scanf("%s", pc->data[pos].tele); printf("請輸入地址:>"); scanf("%s", pc->data[pos].addr); printf("修改成功\n"); }
排序通訊錄
接下來實現(xiàn)排序功能。具體實現(xiàn)如下:
- 定義一個比較函數(shù)CmpByName,用于按照名字來排序聯(lián)系人信息。該函數(shù)的參數(shù)為兩個指向聯(lián)系人信息結(jié)構(gòu)體的指針p1和p2,返回值為兩個名字字符串的比較結(jié)果。
- 在CmpByName函數(shù)中,首先使用assert宏檢查指針p1和p2的有效性,確保程序不會因為無效指針而崩潰。然后使用strcmp函數(shù)比較兩個聯(lián)系人信息結(jié)構(gòu)體中的名字字符串大小,返回比較結(jié)果。
- 定義一個排序函數(shù)SortContact,用于對聯(lián)系人信息進(jìn)行排序。該函數(shù)的參數(shù)為一個指向聯(lián)系人管理系統(tǒng)結(jié)構(gòu)體的指針pc。
- 在SortContact函數(shù)中,首先使用assert宏檢查指針pc的有效性,確保程序不會因為無效指針而崩潰。然后調(diào)用qsort函數(shù)對聯(lián)系人信息進(jìn)行排序,其中pc->data表示聯(lián)系人信息數(shù)組的首地址,pc->sz表示聯(lián)系人信息數(shù)組的大小,sizeof(PeoInfo)表示每個聯(lián)系人信息結(jié)構(gòu)體的大小,CmpByName表示排序函數(shù)。
- 最后輸出排序成功的提示信息。
// 按照名字來排序 int CmpByName(const void* p1, const void* p2) { // 檢查指針有效性 assert(p1 && p2); // 比較名字字符串大小 return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name); } void SortContact(Contact* pc) { // 檢查指針有效性 assert(pc); // 根據(jù)名字來排序 qsort(pc->data, pc->sz, sizeof(PeoInfo), CmpByName); printf("排序成功\n"); }
保存通訊錄
最后實現(xiàn)將聯(lián)系人信息保存到文件中的功能。具體實現(xiàn)如下:
- 函數(shù)定義:函數(shù)名為SaveContact,參數(shù)為一個指向Contact結(jié)構(gòu)體的指針pc。
- 檢查指針有效性:使用assert宏函數(shù)檢查指針pc是否為NULL,如果是NULL則程序會直接終止。
- 打開文件:使用fopen函數(shù)打開名為"contact.dat"的文件,以二進(jìn)制寫入模式(“wb”)打開。返回值為一個指向FILE結(jié)構(gòu)體的指針pf。
- 檢查是否打開成功:使用if語句判斷指針pf是否為NULL,如果是NULL則說明打開文件失敗,使用perror函數(shù)輸出錯誤信息并返回。
- 寫數(shù)據(jù):使用for循環(huán)遍歷pc指向的Contact結(jié)構(gòu)體中的所有聯(lián)系人信息,使用fwrite函數(shù)將每個聯(lián)系人信息寫入文件中。其中,第一個參數(shù)為指向聯(lián)系人信息的指針,第二個參數(shù)為每個聯(lián)系人信息的大小,第三個參數(shù)為寫入的數(shù)量,第四個參數(shù)為指向文件的指針pf。
- 關(guān)閉文件:使用fclose函數(shù)關(guān)閉文件,釋放文件指針pf所占用的資源。將pf賦值為NULL,防止出現(xiàn)野指針。
- 輸出保存成功信息:使用printf函數(shù)輸出保存成功的信息。
void SaveContact(Contact* pc) { // 檢查指針有效性 assert(pc); // 打開文件 FILE* pf = fopen("contact.dat", "wb"); // 檢查是否打開成功 if (pf == NULL) { // 打開文件失敗 perror("SaveContact::fopen"); return; } // 寫數(shù)據(jù),一次寫一個 for (int i = 0; i < pc->sz; ++i) { fwrite(pc->data + i, sizeof(struct PeoInfo), 1, pf); } //關(guān)閉文件 fclose(pf); pf = NULL; printf("保存成功...\n"); }
總結(jié)
- 動態(tài)內(nèi)存管理一定要熟練掌握,包括使用malloc開辟空間,使用realloc擴(kuò)容,使用free函數(shù)釋放空間等。
- 常見的文件操作函數(shù)得會用,包括fopen,fclose以及文件的順序讀寫和隨機(jī)讀寫操作等。
- 順序表的增刪查改、排序打印等常規(guī)操作一定要掌握,通訊錄的本質(zhì)就是順序表。
以上就是基于C語言打造高效通訊錄的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于C語言通訊錄的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解C++的靜態(tài)內(nèi)存分配與動態(tài)內(nèi)存分配
內(nèi)存分配 (Memory Allocation) 是指為計算機(jī)程序或服務(wù)分配物理內(nèi)存空間或虛擬內(nèi)存空間的一個過程,本文主要介紹了C++的靜態(tài)內(nèi)存分配與動態(tài)內(nèi)存分配,感興趣的同學(xué)可以參考閱讀2023-06-06C++中Cbitmap,HBitmap,Bitmap區(qū)別及聯(lián)系
這篇文章主要介紹了C++中Cbitmap,HBitmap,Bitmap區(qū)別及聯(lián)系的相關(guān)資料,需要的朋友可以參考下2015-06-06