柔性数组)动态版通讯录"/>
(使用柔性数组)动态版通讯录
文章目录
- 前言:
- 思维导图
- 思路分析
- 分析一:
- 分析二
- 分析三:
- 分析四:
- 函数分析
- 初始化函数
- 打印函数
- 排序函数
- 添加元素函数
- 查找元素函数
- 删除元素函数
- 修改元素函数
- 全部代码:
- 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;//悬挂pcIn_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行的小程序,你也可以 |
更多推荐
(使用柔性数组)动态版通讯录
发布评论