文章目錄
- 前言:
- 思維導圖
- 思路分析
- 分析一:
- 分析二
- 分析三:
- 分析四:
- 函式分析
- 初始化函式
- 列印函式
- 排序函式
- 添加元素函式
- 查找元素函式
- 洗掉元素函式
- 修改元素函式
- 全部代碼:
- text.c
- contact.h
- contact.c
- 效果展示
- 總結
前言:
博主目前實力有限,博文有什么錯誤,請你斧正!
后面學會EasyX會重新更新本篇博客,0.0.
上一篇博客更新了動態記憶體管理與柔性陣列,因此趁熱打鐵,本通訊錄采用動態函式與柔性陣列,
希望本篇博客對你有幫助,
思維導圖

思路分析
分析一:
我的思路是來一個元素,增加一次空間(realloc),這樣不需要預留空間 了,順便在添加元素的時侯,排序元素,0.0
分析二
通訊錄中包含 很多人,而每個人又有很多類資訊,因此我們創建2個結構體(一個也行,建立結構陣列):一個是人資訊的集合,一個記錄
有效的人數與人結構柔性陣列的通訊錄enum whole//存放全域作用的常量 { Name_max=20, Age_max=10, Sex_max=20, Tel_max=20, Exit=0, Add, Del, Modify, Show, Search }; struct peo { char name[Name_max]; char age[Age_max]; char sex[Sex_max]; char tel[Tel_max]; }; struct cont { size_t sz; struct peo date[0];//柔性陣列,這里用realloc };
分析三:
在洗掉,修改,都需要對結構體進行元素
查找(我用的二分查找法,需要陣列有一定順序),因此需要排序陣列,但是什么時候排序呢?我的思路是在添加元素的時候就排序陣列,而排序我用的是 模擬qsort的方法,需要2個種子,
分析四:
我們在
函式中動態記憶體開辟時,實參可能不產生任何影響**(形參只是實參的拷貝,我只是給形式變數傳一個資料)**,為了避免這種情況,在某些函式我們通過使用二級指標,可以改變實參的指向,另外在這個函式中:我們可以先用中間變數去 realloc,產生我們想要作用之后,再用實參指向新的申請空間,
函式分析
初始化函式
- 通過二級指標的方法,改變實參的指向,
void In_Bein(struct cont** pc) { assert(pc); struct cont *p = (struct cont*)malloc(sizeof(struct cont));//這里只需要給sz申請空間就行了 //后面增加元素的時候再未peo開辟空間 if (NULL == p) { printf("%s\n", strerror(errno)); exit(1); } p->sz = 0; *pc = p; //p是區域變數,因此函式堆疊幀結束后,就成為野指標,但是那快動態申請的空間沒有free, // 但是我們通過 *pc=p,這步,在程式結束,最后free就行了, }
列印函式
void My_Show(struct cont* pc) { assert(pc);//斷言防止傳入NULL指標 for (size_t i = 0; i < pc->sz; i++) { printf("姓名:%s\n", ((pc -> date)+i)->name); printf("年齡:%s\n", ((pc-> date)+i)->age); printf("性別:%s\n", ((pc->date)+i)->sex); printf("電話:%s\n", ((pc->date)+i)->tel); } }
排序函式
復寫qsort的使用見我另外一篇博客:
在排序的時候,可能會碰到同名的情況,因此我們比較電話號碼就行了,畢竟電話號碼是唯一的,因此需要2個比較函式(我稱謂
種子),(排序是從小到大,想從大到小,改變種子就行)int compare1(const void* e1, const void* e2)//種子1 比較名字 //比較結構體2元素中成員 名字 { assert(e1 && e2);//防止傳入NULL指標 return strcmp(((struct peo*)e1)->name, ((struct peo*)e2)->name); } int compare2(const void* e1, const void* e2)//種子2 比較電話 { assert(e1 && e2); return strcmp(((struct peo*)e1)->tel, ((struct peo*)e2)->tel); } void swap(void* e1, void* e2, size_t width)//交換2元素所占記憶體位元組中的內容 { assert(e1 && e2); for (size_t i = 0; i < width; i++)//依次交換e1,e2中的位元組內容 { unsigned char tmp = *((unsigned char*)e1 + i); *((unsigned char*)e1 + i) = *((unsigned char*)e2 + i); *((unsigned char*)e2 + i) = tmp; } } void my_qsort(void* base, size_t num,size_t width ) { assert(base);//防止傳入NULL指標 for (size_t i = 0; i < num-1; i++) { size_t min = i; for (size_t j = i + 1; j < num; j++) { //傳入的是date,因此強轉(char*)后利用width就行了 if (0 < compare1( (char *)base + min * width, (char*)base + j * width)) { min = j; } else if (0 == compare1((char*)base + min * width, (char*)base + j * width))//一旦重名就檢測電話 { if (0 < compare2((char*)base + min * width, (char*)base + j * width)) { min = j; } } } if (min != i) { swap((char *)base + i * width, (char *)base + min * width,width); } } }
添加元素函式
- 添加元素,必然需要重新開辟空間,需要realloc,
- 我們要使實參改變指向,必然二級指標,
void My_Add(struct cont** pc) { assert(pc);//斷言防止傳入NULL指標 struct cont* p = (struct cont*)realloc(*pc, sizeof(struct cont) + ((*pc)->sz+1) * sizeof(struct peo)); if (NULL == p) { printf("%s\n", strerror(errno));//列印錯誤資訊 exit(1); } printf("請輸入姓名:"); scanf("%s", (p->date + p->sz)->name); printf("\n"); printf("請輸入性別:"); scanf("%s", (p->date + p->sz)->sex); printf("\n"); printf("請輸入年齡:"); scanf("%s", (p->date + p->sz)->age); printf("\n"); printf("請輸入電話:"); scanf("%s", (p->date + p->sz)->tel); p->sz++; *pc = p; my_qsort(p->date, p->sz, sizeof(struct peo));//排序新增后的元素 } void My_Add(struct cont** pc) { assert(pc);//斷言防止傳入NULL指標 struct cont* p = (struct cont*)realloc(*pc, sizeof(struct cont) + ((*pc)->sz+1) * sizeof(struct peo)); if (NULL == p) { printf("%s\n", strerror(errno));//列印錯誤資訊 exit(1); } printf("請輸入姓名:"); scanf("%s", (p->date + p->sz)->name); printf("\n"); printf("請輸入性別:"); scanf("%s", (p->date + p->sz)->sex); printf("\n"); printf("請輸入年齡:"); scanf("%s", (p->date + p->sz)->age); printf("\n"); printf("請輸入電話:"); scanf("%s", (p->date + p->sz)->tel); p->sz++; *pc = p; my_qsort(p->date, p->sz, sizeof(struct peo)); }
查找元素函式
二分查找必然需要陣列一定順序.
回傳一個值的原因是為了其它函式的實作,
int My_Search(struct cont* pc,const char *str1,const char *str2)//二分查找 { assert(pc && str1 && str2);//斷言防止傳入NULL指標 size_t left = 0; size_t right = pc->sz - 1; while (left <= right) { size_t mid = (left + right) / 2; if ((strcmp((pc->date + mid)->name, str1)==0)&& (strcmp((pc->date + mid)->tel, str2) == 0)) { printf("找到了\n"); printf("%d\n", mid); return mid; } else if((strcmp((pc->date + mid)->name, str1)<0)||((strcmp((pc->date + mid)->name, str1)==0) && (strcmp((pc->date + mid)->tel, str2)<0))) { left = mid + 1; } else if ((strcmp((pc->date + mid)->name, str1) > 0) || ((strcmp((pc->date + mid)->name, str1) == 0 && (strcmp((pc->date + mid)->tel, str2) > 0)))) { right = mid - 1; } } printf("查無此人\n"); return -1; }
洗掉元素函式
- 洗掉必然需要查找是否存在,而查找又需要排序,哈哈~~~0.0.
void My_Del(struct cont** pc) { assert(pc);//斷言防止傳入NULL指標 char You_name[Name_max] = {0}; char You_tel[Tel_max] = {0}; printf("請輸入要洗掉的姓名與電話\n"); scanf("%s", You_name); scanf("%s", You_tel); int ret = My_Search(*pc,You_name,You_tel); if (ret!=-1) { for (size_t i = ret; i < (*pc)->sz-1; i++) { *((*pc)->date + i) = *((*pc) -> date+i + 1); } struct cont* p = (struct cont*)realloc(*pc, ((*pc)->sz - 1) * sizeof(struct peo)); if (NULL == p) { printf("%s\n", strerror(errno)); exit(1); } p->sz--; *pc = p; } else { printf("請重新輸入\n"); } }
修改元素函式
- 修改必然查找…(套娃–哈哈 ~ ~~)
void My_Modify(struct cont* pc) { assert(pc);//斷言防止傳入NULL指標 char You_name[Name_max] = {0};//要初始化下,strcpy的原因 char You_tel[Tel_max] = {0}; char You_sex[Sex_max] = {0}; char You_age[Age_max] = {0}; while (1) { printf("請輸入要修改的姓名與電話\n"); scanf("%s", You_name); scanf("%s", You_tel); int ret = My_Search(pc, You_name, You_tel); if (ret != -1) { printf("請輸入要修改的姓名:"); scanf("%s", You_name); strcpy((pc->date + ret)->name, You_name); printf("請輸入要修改的年齡:"); scanf("%s", You_age); strcpy((pc->date + ret)->age , You_age); printf("請輸入要修改的性別:"); scanf("%s", You_sex); strcpy((pc->date + ret)->sex, You_sex); printf("請輸入要修改的電話:"); scanf("%s", You_tel); strcpy((pc->date + ret)->tel, You_tel); printf("修改成功\n"); return; } else { printf("%s\t%s\n", You_name, You_tel); } } }
全部代碼:
text.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "contact.h" void menu()//提示選單 { printf("****************************************************\n"); printf("*************動態通訊錄*****************************\n"); printf("****************************************************\n"); printf("*******1.Add(添加) 2. Del(洗掉)*******\n"); printf("****** 3.Modify(糾正) 4.Show(列印)*******\n"); printf("*******5.Search(尋找) *******\n"); printf("*******0.Exit(退出) ************\n"); printf("****************************************************\n"); } int main () { int Input = 0; struct cont* pc = NULL;//懸掛pc In_Bein(&pc);//因為pc NULL的原因,我們必須讓它指向一段動態的記憶體,并初始化, do//先列印提示選單,供玩家判斷, { menu(); printf("請輸入你的選擇:\n"); scanf("%d", &Input); switch (Input) { case Add:My_Add(&pc); break; case Del:My_Del(&pc); break; case Modify:My_Modify(pc); break; case Show:My_Show(pc); break; case Search: { char You_name[Name_max] = {0}; char You_tel[Tel_max] = {0}; printf("請輸入要找的的姓名與電話\n"); scanf("%s", You_name); scanf("%s", You_tel); My_Search(pc, You_name, You_tel); }; break; case Exit://exit是C語言內置的關鍵字,因此Exit,而不是 exit. { system("cls");//清空螢屏 printf("\t\t~~~~~~~感謝使用博主的動態通許錄!!!!~~~~~\n"); }; break; default:printf("輸入錯誤,請重新輸入\n"); break; } } while (Input); free(pc); pc = NULL; return 0; }
contact.h
#pragma once #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> enum whole//存放全域作用的常量 { Name_max=20, Age_max=10, Sex_max=20, Tel_max=20, Exit=0, Add, Del, Modify, Show, Search }; struct peo { char name[Name_max]; char age[Age_max]; char sex[Sex_max]; char tel[Tel_max]; }; struct cont { size_t sz; struct peo date[0];//柔性陣列,這里用realloc }; void In_Bein(struct cont** pc); void My_Show(struct cont* pc); void My_Add(struct cont** pc); void My_Del(struct cont** pc); int My_Search(struct cont* pc,const char*str1,const char *str2); void My_Modify(struct cont* pc); int compare1(const void* e1, const void* e2);//種子1 比較名字 //比較結構體2元素中成員 名字 int compare2(const void* e1, const void* e2);//種子2 比較學號 void swap(void* e1, void* e2, size_t width); void my_qsort(void* base,size_t num,size_t width);//有2個種子就不需要 再來個引數了
contact.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "contact.h" void In_Bein(struct cont** pc) { assert(pc);//斷言防止傳入NULL指標 struct cont* p = (struct cont*)malloc(sizeof(struct cont));//這里只需要給sz申請空間就行了 //后面增加元素的時候再未peo開辟空間 if (NULL == p) { printf("%s\n", strerror(errno)); exit(1); } p->sz = 0; *pc = p; //p是區域變數,因此函式堆疊幀結束后,就成為野指標,但是那快動態申請的空間沒有free, // 但是我們通過 *pc=p,這步,在程式結束,最后free就行了, } void My_Show(struct cont* pc) { assert(pc);//斷言防止傳入NULL指標 for (size_t i = 0; i < pc->sz; i++) { printf("姓名:%s\n", ((pc -> date)+i)->name); printf("年齡:%s\n", ((pc-> date)+i)->age); printf("性別:%s\n", ((pc->date)+i)->sex); printf("電話:%s\n", ((pc->date)+i)->tel); } } void My_Add(struct cont** pc) { assert(pc);//斷言防止傳入NULL指標 struct cont* p = (struct cont*)realloc(*pc, sizeof(struct cont) + ((*pc)->sz+1) * sizeof(struct peo)); if (NULL == p) { printf("%s\n", strerror(errno));//列印錯誤資訊 exit(1); } printf("請輸入姓名:"); scanf("%s", (p->date + p->sz)->name); printf("\n"); printf("請輸入性別:"); scanf("%s", (p->date + p->sz)->sex); printf("\n"); printf("請輸入年齡:"); scanf("%s", (p->date + p->sz)->age); printf("\n"); printf("請輸入電話:"); scanf("%s", (p->date + p->sz)->tel); p->sz++; *pc = p; my_qsort(p->date, p->sz, sizeof(struct peo));//排序新增后的元素 } int My_Search(struct cont* pc,const char *str1,const char *str2)//二分查找 { assert(pc && str1 && str2);//斷言防止傳入NULL指標 size_t left = 0; size_t right = pc->sz - 1; while (left <= right) { size_t mid = (left + right) / 2; if ((strcmp((pc->date + mid)->name, str1)==0)&& (strcmp((pc->date + mid)->tel, str2) == 0)) { printf("找到了\n"); printf("%d\n", mid); return mid; } else if((strcmp((pc->date + mid)->name, str1)<0)||((strcmp((pc->date + mid)->name, str1)==0) && (strcmp((pc->date + mid)->tel, str2)<0))) { left = mid + 1; } else if ((strcmp((pc->date + mid)->name, str1) > 0) || ((strcmp((pc->date + mid)->name, str1) == 0 && (strcmp((pc->date + mid)->tel, str2) > 0)))) { right = mid - 1; } } printf("查無此人\n"); return -1; } void My_Del(struct cont** pc) { assert(pc);//斷言防止傳入NULL指標 char You_name[Name_max] = {0}; char You_tel[Tel_max] = {0}; printf("請輸入要洗掉的姓名與電話\n"); scanf("%s", You_name); scanf("%s", You_tel); int ret = My_Search(*pc,You_name,You_tel); if (ret!=-1) { for (size_t i = ret; i < (*pc)->sz-1; i++) { *((*pc)->date + i) = *((*pc) -> date+i + 1); } struct cont* p = (struct cont*)realloc(*pc, ((*pc)->sz - 1) * sizeof(struct peo)); if (NULL == p) { printf("%s\n", strerror(errno)); exit(1); } p->sz--; *pc = p; } else { printf("請重新輸入\n"); } } void My_Modify(struct cont* pc) { assert(pc);//斷言防止傳入NULL指標 char You_name[Name_max] = {0};//要初始化下,strcpy的原因 char You_tel[Tel_max] = {0}; char You_sex[Sex_max] = {0}; char You_age[Age_max] = {0}; while (1) { printf("請輸入要修改的姓名與電話\n"); scanf("%s", You_name); scanf("%s", You_tel); int ret = My_Search(pc, You_name, You_tel); if (ret != -1) { printf("請輸入要修改的姓名:"); scanf("%s", You_name); strcpy((pc->date + ret)->name, You_name); printf("請輸入要修改的年齡:"); scanf("%s", You_age); strcpy((pc->date + ret)->age , You_age); printf("請輸入要修改的性別:"); scanf("%s", You_sex); strcpy((pc->date + ret)->sex, You_sex); printf("請輸入要修改的電話:"); scanf("%s", You_tel); strcpy((pc->date + ret)->tel, You_tel); printf("修改成功\n"); return; } else { printf("%s\t%s\n", You_name, You_tel); } } } int compare1(const void* e1, const void* e2)//種子1 比較名字 //比較結構體2元素中成員 名字 { assert(e1 && e2);//防止傳入NULL指標 return strcmp(((struct peo*)e1)->name, ((struct peo*)e2)->name); } int compare2(const void* e1, const void* e2)//種子2 比較電話 { assert(e1 && e2); return strcmp(((struct peo*)e1)->tel, ((struct peo*)e2)->tel); } void swap(void* e1, void* e2, size_t width)//交換2元素所占記憶體位元組中的內容 { assert(e1 && e2); for (size_t i = 0; i < width; i++)//依次交換e1,e2中的位元組內容 { unsigned char tmp = *((unsigned char*)e1 + i); *((unsigned char*)e1 + i) = *((unsigned char*)e2 + i); *((unsigned char*)e2 + i) = tmp; } } void my_qsort(void* base, size_t num,size_t width ) { assert(base);//防止傳入NULL指標 for (size_t i = 0; i < num-1; i++) { size_t min = i; for (size_t j = i + 1; j < num; j++) { //傳入的是date,因此強轉(char*)后利用width就行了 if (0 < compare1( (char *)base + min * width, (char*)base + j * width)) { min = j; } else if (0 == compare1((char*)base + min * width, (char*)base + j * width))//一旦重名就檢測電話 { if (0 < compare2((char*)base + min * width, (char*)base + j * width)) { min = j; } } } if (min != i) { swap((char *)base + i * width, (char *)base + min * width,width); } } }
效果展示


總結
| 弄清思路,代碼的實作只是時間與思考的問題, |
|---|
動態函式的語法容易理解,但是實操卻會有很多問題堅持,除錯,思考,測驗,反復這個400行的小程式,你也可以 |
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/306283.html
標籤:其他
上一篇:CSS復合選擇器
