我们先思考一个通讯录都有那些信息,很明显通讯录记录的是人
人有哪些信息呢
这里我就写5个吧,分别是姓名,年龄,电话,性别,地址
然后我们把他们写成一个结构体,最好定义在头文件里,这样在使用的时候更方便
我们还可以把要使用的一些常用的或者要修改的常量定义成枚举,后续有什么要添加的,直接就能在枚举中添加
//枚举的一些关键常量 enum NUM { NAME = 20, //名字 TELE = 12, //电话 SEX = 10, //性别 ADDR = 100, //地址 ADD = 1,DEL = 2,SEARCH = 3,MODIFY = 4,SHOW = 5,SORT = 6,EXIT = 0,CLEAR =7,//菜单 //ARRDATA = 100 SIZE = 3, //通讯录初始容量大小 SIZEADD = 2 //每次扩容的增加量 }; //人的信息 typedef struct peo { char name[NAME]; //姓名 int age; //年龄 char tele[TELE]; //电话 char sex[SEX]; //性别 char addr[ADDR]; //地址 }peo;这里的枚举内容我直接把参数都写里了,后续在思考的过程就不再改动了
接下来思考,光有一个人的信息是不够的,无法完整表达一个通讯录,当然,由于我们只是简单实现,我们就只再记录通讯录的人员数量和通讯录的容量
把他们简单封装成一个结构体
typedef struct contact { peo* data; //人的相关数据 size_t count; //成员数量 size_t capacity; //通讯录容量 }contact;这里我都是进行了重命名了的,方便后续的使用
这些都是头文件里面要用的,还有一些库函数,我就在这里提前定义了
#include<stdio.h> #include<string.h> #include<assert.h> #include<stdlib.h> #include<errno.h>然后我们写主函数
我们考虑通讯录运行一定要有菜单
然后我们用switch语句写分支结构
我们需要用户主动输入选择,所以我们还需要一个变量接收,这个变量还可以用于switch语句的判断
菜单我们可以单独封装一个函数
可以实现增加联系人,删除联系人,查找联系人,修改联系人,显示联系人,按名字排序联系人,清空联系人,和退出保存,将数据保存到文件中,把这些功能都封装成函数
还得创建一个结构体变量用于存放通讯录的数据
还需要一个函数对结构体进行初始化,并把保存在文件中的信息放入到结构体中
于是主函数如下
#include"contact.h" static void menu() //菜单函数 { printf("****************************************\n"); printf("****************************************\n"); printf("****************************************\n"); printf("*** 1.add 2.del ******\n"); printf("*** 3.search 4.modify ******\n"); printf("*** 5.show 6.sort ******\n"); printf("*** 0.exit 7.clear ******\n"); printf("****************************************\n"); printf("****************************************\n"); printf("****************************************\n"); } int main() { int input = 0; //存放菜单选择输入的值 contact con; //创建存放通讯录数据的结构体 init_contact(&con);//初始化结构体 do { menu(); printf("请输入要进行的功能\n"); scanf("%d", &input); //菜单的选择输入 switch (input) //菜单选择的判断逻辑 { case ADD: add_contact(&con); //添加函数 break; case DEL: del_contact(&con); //删除函数 break; case SEARCH: search_contact(&con); //查找函数 break; case MODIFY: modify_contact(&con); //修改函数 break; case SHOW: show_contact(&con); //显示函数 break; case SORT: sort_contact(&con); //排序函数 break; case CLEAR: clear_contact(&con); //清空函数 break; case EXIT: save_contact(&con); //保存函数 destroycontact(&con); //推出后内存的销毁 printf("程序退出\n"); break; default: printf("选择错误,请重新选择\n");//其他逻辑判断 break; } } while (input)//do while的循环判断 return 0; }主函数别忘了引用头文件
接下来我们封装函数
把函数单独放在一个源文件
#include"contact.h" //初始化函数 //对于初始化我们不仅要初始化结构体内容,还要把之前保存在文件中的信息放入到结构体中,当然还涉及到一个动态扩容的问题,我们的结构体初始设计的是有容量的,如果里面放的信息多了,还要涉及到扩容的问题, 这些都要设计函数 static void expandCapacity(contact* con) //扩容 { assert(con); //断言防止空指针 if (con->capacity == con->count) //容量判断 { void* p = 0; p = realloc(con->data, (con->capacity + SIZEADD) * sizeof(peo));//追加信息容量 if (NULL == p) //报错判断 { printf("expandCapacity::%s\n", strerror(errno)); return; } con->capacity += SIZEADD;//修改容量标记 con->data = (peo*)p; //修改指针位置(其实没变) printf("增容成功\n"); } } static int read_conact(contact* con) //读取文件中保存的通讯录信息 { static void expandCapacity(contact * con); //函数声明 FILE* p = fopen("contact.txt", "r"); //打开文件 if (p == NULL) //报错判断 { return 1; } peo a = { 0 }; while (fread(&a, sizeof(peo), 1, p) == 1)//循环提取信息,这里取决于保存函数的写法 { expandCapacity(con); //扩容 con->data[con->count] = a;//赋值 (con->count)++;//成员计数加一 } fclose(p); p = NULL;//关闭文件,指针空置 return 0; } void init_contact(contact* con) //初始化函数 { static int read_conact(contact * con);//函数调用声明 assert(con);////断言防止空指针 con->count = 0;//初始化人数 void* p = 0;//内存指针 p = calloc(SIZE, sizeof(peo));//创建内存 if (NULL == con->data)//报错判断 { printf("init_contact:calloc:%s\n", strerror(errno)); return; } con->data = (peo*)p;//初始化人信息指针 con->capacity = SIZE;//初始化容量 if (read_conact(con) == 1)//读取之前的数据 { perror("读取旧数据失败");//问题判断 return; } } //初始化我们已经完成了 //接下来我们写添加函数 void add_contact(contact* con) { static void expandCapacity(contact * con); //函数声明 assert(con);//断言防止空指针 expandCapacity(con);//扩容判断 printf("请输入姓名\n"); scanf("%s", (con->data+con->count)->name); printf("请输入年龄\n"); scanf("%d", &((con->data + con->count)->age)); printf("请输入电话\n"); scanf("%s", con->data[con->count].tele); printf("请输入性别\n"); scanf("%s", con->data[con->count].sex); printf("请输入地址\n"); scanf("%s", con->data[con->count].addr);//这些都是信息是输入 con->count++;//计数加一 printf("增加成功\n"); } //接下来我们写显示函数 void show_contact(contact* con) { assert(con);//断言防止空指针 int i = 0; printf("%-20s\t%-5s\t%5s\t%12s\t%30s\n", "姓名", "年龄", "电话", "性别", "地址"); //显示时方便观察,打印一个标签 for (i = 0; i < con->count; i++)//循环打印人员信息 { printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", con->data[i].name, con->data[i].age, con->data[i].tele, con->data[i].sex, con->data[i].addr); } } //查找函数 //查找函数不仅要能查找,还要能显示,我们分成两个函数,因为删除也需要查找 //查找部分 static int find_by_name(const contact* con,const char* arr) //依据名字查找 { assert(con && arr);//断言防止空指针 int i = 0; for (i = 0; i < con->count; i++)//循环遍历查找 { if (0 == strcmp(con->data[i].name, arr)) { return i;//找到返回下标 } } return -1;//找不到返回-1 } //查找 void search_contact(contact* con) { static int find_by_name(const contact * con, const char* arr);//函数声明 assert(con);//断言防止空指针 if (con->count == 0)//无联系人情况 { printf("无联系人可查找\n"); return; } printf("请输入要查找的人的姓名\n"); char arr[NAME] = { 0 }; scanf("%s", arr); int a = find_by_name(con, arr);//套用查找函数 if (a == -1) { printf("要查找的人不存在\n"); return; } printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "年龄", "电话", "性别", "地址"); printf("%-20s\t%-5d\t%-5s\t%-12s\t%-30s\n", con->data[a].name, con->data[a].age, con->data[a].tele, con->data[a].sex, con->data[a].addr);//显示部分 } //删除 void del_contact(contact* con) { static int find_by_name(const contact * con, const char* arr);//函数声明 assert(con);//断言防止空指针 char arr[NAME] = { 0 }; if (con->count == 0)//无联系人情况 { printf("无联系人可删除\n"); return; } printf("请输入要删除人的名字\n"); scanf("%s", arr); int a = find_by_name(con,arr);//函数回调 if (a == -1) { printf("要删除的人不存在\n"); return; } for (; a < con->count - 1; a++)//删除本质就是把后面的信息提前,覆盖掉这个下标的信息 //当然也还有其他的写法 { con->data[a] = con->data[a + 1]; } con->count--; printf("删除成功\n"); } //修改函数 //修改也要先找 void modify_contact(contact* con) { static int find_by_name(const contact * con, const char* arr);//函数声明 assert(con);//断言防止空指针 if (con->count == 0) { printf("无联系人可修改\n");//没有的情况判断 return; } printf("请输入要修改的人的姓名\n"); char arr[NAME] = { 0 }; scanf("%s", arr); int a = find_by_name(con, arr);//调用函数 if (a == -1) { printf("要修改的人不存在\n"); return; } printf("请输入修改姓名\n"); scanf("%s", con->data[a].name); printf("请输入修改年龄\n"); scanf("%d", &(con->data[a].age)); printf("请输入修改电话\n"); scanf("%s", con->data[a].tele); printf("请输入修改性别\n"); scanf("%s", con->data[a].sex); printf("请输入修改地址\n"); scanf("%s", con->data[a].addr); printf("修改成功\n");//信息的修改 } //排序函数 //这里我使用了qsort static int cmp_peo_by_name(const void* e1, const void* e2)//qsort调用的排序函数 { return strcmp(((const peo*)e1)->name, ((const peo*)e2)->name); } void sort_contact(contact* con)//本体 { static int cmp_peo_by_name(const void* e1, const void* e2);//函数调用 assert(con);//断言 if (con->count == 0) { printf("无联系人可排序\n");//没人的情况 return; } printf("正在按姓名排序\n"); qsort(con->data, con->count, sizeof(peo), cmp_peo_by_name);//qsort排序 printf("排序成功\n"); show_contact(con);//这里不声明是因为这些函数都会在头文件中声明,但内些静态函数不会 } //销毁,保存,清空 void destroycontact(contact* con)//释放内存 { assert(con); free(con->data); con->data = NULL; } void save_contact(const contact* con)//保存信息 { assert(con); FILE* p = fopen("contact.txt", "wb"); if (p == NULL) { perror("保存失败:save_contact:fopen"); return; } fwrite(con->data, sizeof(peo), con->count, p); fclose(p); p = NULL; } void clear_contact(contact* con)//清空文件信息和当前结构体信息 { assert(con); FILE* p = fopen("contact.txt", "w"); if (p == NULL) return; fclose(p); p = NULL; con->count = 0; memset(con->data,0, sizeof(peo)); printf("已全部清除\n"); }最后我们再在头文件中声明一下这些函数,为了调用