博主目前实力有限,博文有什么错误,请你斧正!
后面学会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); } } }
#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; }
#pragma once #include
#include #include #include 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个种子就不需要 再来个参数了
#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行的小程序,你也可以 |