> 作者简介:დ旧言~,目前大一,现在学习Java,c,c++,Python等
> 座右铭:松树千年终是朽,槿花一日自为荣。
> 望小伙伴们点赞👍收藏✨加关注哟💕💕
🌟前言
当我们需要办一些业务时,需要录入我们的个人信息,我们的个人信息放在一个系统中,我们简称这个系统为通讯录,而这个通讯录可以存储我们的名字,性别,电话,地址....等等等。今天我们实现一个简单的通讯录。
🌙主体
咱们需要建立一个项目,项目里面有test.c,Contact.h,Contact.c。
其实这里contact.c是重点,咱们一个一个文件看看它们是如何实现的。
🌠contact.h的实现
这个文件用来声明的,其中包括函数声明,包含头文件,实现动态通讯录,枚举的实现,定义元素类型。咱们逐个实现。
💤定义枚举和元素类型
定义枚举是为了方便使用switch,枚举第一个元素以0为起始位置。
//定义枚举,方便使用switch改变 enum OPTION { EXIT,//0 ADD,//1 DEL,//2 SEARCH,//3 MODIFY,//4 SHOW,//5 SORT//6 };
通讯录需要存入的数据有名字,性别,电话,地址,因此来定义类型。
//宏定义 #define MAX 100//为静态通讯录服务 #define MAX_NAME 20//名字的最大值 #define MAX_SEX 5//性别的最大值 #define MAX_TELE 12//电话的最大值 #define MAX_ADDR 30//地址的最大值 #define DEFAULT_SZ 3//开始通讯录空间大小 #define INC_SZ 2//开辟空间数值大小 //定义通讯录所包含的元素类型 typedef struct PeoInfo { char name[MAX_NAME]; int age; char sex[MAX_SEX]; char tele[MAX_TELE]; char addr[MAX_ADDR]; }PeoInfo;
这里用typedef来给结构体重新定义名字。
💤动态通讯录
正常我们写入的代码是这样的
//静态通讯录 //typedef struct Contact //{ // PeoInfo data[MAX]; // int sz; //}Contact;
这样就把通讯录写死了,当通讯录的空间不够时,无法实现扩容,因此我们需要一个动态的通讯录,而动态的通讯录有什么呢?
1.存放数据的空间(这里需要结构体指针,才能指向下一个元素(这里在动态内存管理已经讲解过了哟))
2.当前放的有效元素的个数
3.通讯录当前的最大容量
//动态通讯录 typedef struct Contact { PeoInfo* data;//指向了存放数据的空间 int sz;//记录的当前放的有效元素的个数 int capacity;//通讯录当前的最大容量 }Contact;
💤函数声明
这里就直接上代码了,具体内容咱们到contact.c里面讲解
//函数声明 //初始化通讯录 void InitContact(Contact* pc); //增加联系人 void AddContact(Contact* pc); //显示所有联系人的信息 void ShowContact(const Contact* pc); //删除指定联系人 void DelContact(Contact* pc); //查找指定联系人 void SearchContact(const Contact* pc); //修改指定联系人 void ModifyContact(Contact* pc); //销毁通讯录 void DestroyContact(Contact* pc); //保存通讯录信息到文件 void SaveContact(Contact* pc); //排序 void SortContact(Contact* pc)
🌠test.c的实现
这里的内容和博主写的三子棋与扫雷极其相似,首先打印菜单,其次能实现循环(进行多次玩),最后根据自己选定内容进行不同的程序。
1.首先需要包含头文件,包含#include"contact.h"
2.其次写上简单的void menu(),实现打印菜单
3.然后写上void test(),来实现通讯录的各种功能
4.最后写上主函数
#include"contact.h" //续写菜单 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"); } void test() { //定义变量来实现输入 int input = 0; //创建结构体 Contact con; //初始化通讯录 InitContact(&con); //循环 do { //先打印菜单 menu(); printf("请选择->"); scanf("%d", &input); switch (input) { case ADD: AddContact(&con); break; case DEL: DelContact(&con); break; case SEARCH: SearchContact(&con); break; case MODIFY: ModifyContact(&con); break; case SHOW: ShowContact(&con); break; case SORT: //排序 //按照名字排序? //按照年龄排序? break; case EXIT: SaveContact(&con); DestroyContact(&con); printf("退出通讯录\n"); break; default: printf("选择错误, 重新选择\n"); break; } } while (input); } //主函数 int main() { //调用函数 test(); return 0; }
🌠contact.c的实现(重点)
其实contact.c是通讯录的核心,也是通讯录最难的部分,咱们把这部分分成一块一块,一起来学习这部分。
💤初始化讯录
如果我们写上静态版本的通讯录就无法实现录入多个用户信息,因此我们只能续写动态版本的通讯录。
💦1.需要为data(指向了存放数据的空间)开辟空间
💦2.判断data是否为空,防止在增删查找本身为空
💦3.对sz(记录的当前放的有效元素的个数)和capacity(通讯录当前的最大容量)初始化
💦4.保存在文件(调用函数,后面会讲解)
//静态版本 //void InitContact(Contact* pc) //{ // assert(pc); // memset(pc->data, 0, sizeof(pc->data)); // pc->sz = 0; //} //初始化通讯录(动态) void InitContact(Contact* pc) { //判断pc是否为空 assert(pc); //开辟空间 pc->data = (PeoInfo*)malloc(sizeof(PeoInfo)); //判断是否为空 if (pc->data == NULL) { perror("InitContact"); return 0; } //初始化 pc->sz = 0; pc->capacity = DEFAULT_SZ; //文件中保存的信息加载到通讯录中 LoadContact(pc); }
💤保存文件
这里因为还没讲到文件管理,大家就算看看啦。
void LoadContact(Contact* pc) { //打开文件 FILE* pf = fopen("contact.dat", "rb"); if (pf == NULL) { perror("LoadContact"); return; } //读文件 PeoInfo tmp = { 0 }; while (fread(&tmp, sizeof(PeoInfo), 1, pf)) { //这里如果内存不足需要扩容 if (0 == CheckCapacity(pc)) return; pc->data[pc->sz] = tmp; pc->sz++; } //关闭文件 fclose(pf); pf = NULL; } //保存通讯录信息到文件 void SaveContact(Contact* pc) { FILE* pf = fopen("contact.dat", "wb"); if (pf == NULL) { perror("SaveContact"); return; } //写数据 int i = 0; for (i = 0; i < pc->sz; i++) { fwrite(pc->data + i, sizeof(PeoInfo), 1, pf); } //关闭文件 fclose(pf); pf = NULL; }
💤扩容
当我们的内存不够时我们就需要括容。我们这里的sz(记录的当前放的有效元素的个数),等于capacity(通讯录当前的最大容量)时,就需要扩容。这里我们用realloc来开辟空间,做到对动态开辟内存大小的调整。
💦1.开辟空间,采用结构体指针来接收开辟的空间。
💦2.判断是否开好相应的空间。
💦3.赋值
//是否需要扩容 int CheckCapacity(Contact* pc) { //判断sz是否等于capacity if (pc->sz == pc->capacity) { //开辟空间 PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo)); //判断 if (ptr == NULL) { perror("CheckCapacity"); return; } else { pc->data = ptr; pc->capacity += INC_SZ; printf("增容成功\n"); return 1; } } }
💤增加联系人
这个函数我们已经熟清熟路了,这里我们只需注意sz(记录的当前放的有效元素的个数)需要++,因为需要指向下一个元素的位置。
//增加联系人 void AddContact(Contact* pc) { //断言 assert(pc); //如果通讯录为空直接跳出循环 if (CheckCapacity(pc) == 0) { return; } //输入 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); //当空间不够时需要扩容 CheckCapacity(pc); //指向下个元素 pc->sz++; printf("成功增加联系人\n"); }
💤显示所有联系人的信息
这里和打印没啥区别,太简单了。
//显示所有联系人的信息 void ShowContact(const Contact* pc) { //断言 assert(pc); int i = 0; //打印列标题 printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址"); //打印数据 for (i = 0; i < pc->sz; i++) { printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr); } }
💤删除指定联系人
因为这里删除指定联系人和查找指定联系人都需要查找指定的人,因此我们这里直接再写一个函数,来遍历整个通讯录。
我们采用向前覆盖的方式来删除指定联系人。这里和数据结构的重合了,大家有兴趣可以看看博主写的数据结构相关内容。
//查找元素下标 static int FindByName(const Contact* pc, char name[]) { int i = 0; for (i = 0; i < pc->sz; i++) { if (strcmp(pc->data[i].name, name) == 0) { return i;//找到了 } } return -1;//找不到 } //删除指定联系人 void DelContact(Contact* pc) { if (pc->sz == 0) { printf("通讯录为空,无法删除\n"); return; } char name[MAX_NAME] = { 0 }; assert(pc); //删除 printf("请输入要删除的人名字:>"); scanf("%s", name); //找到要删除的人 int del = FindByName(pc, name); if (del == -1) { printf("要删除的人不存在\n"); return; } int i = 0; //删除坐标位del的联系人(向前覆盖) for (i = del; i < pc->sz - 1; i++) { pc->data[i] = pc->data[i + 1]; } //要向后退一个元素 pc->sz--; printf("成功删除联系人\n"); }
注意需要让sz(记录的当前放的有效元素的个数)--.
💤查找指定联系人
有了删除指定联系人的函数,实现这个函数太简单了。
//查找指定联系人 void SearchContact(const Contact* pc) { //断言 assert(pc); char name[MAX_NAME] = { 0 }; printf("请输入要查找人的名字:>"); scanf("%s", name); int pos = FindByName(pc, name); if (pos == -1) printf("要查找的人不存在\n"); else { printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址"); //打印数据 printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); } }
💤修改指定联系人
有了删除指定联系人的函数,实现这个函数太简单了。
//修改指定联系人 void ModifyContact(Contact* pc) { //断言 assert(pc); char name[MAX_NAME] = { 0 }; printf("请输入要修改人的名字:>"); scanf("%s", name); int pos = FindByName(pc, name); if (pos == -1) printf("要修改的人不存在\n"); else { 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"); } }
💤销毁通讯录
当我们不再使用通讯录了,就要释放内存,所有的元素置0。
//销毁通讯录 void DestroyContact(Contact* pc) { //释放 free(pc->data); //置为空和0 pc->data = NULL; pc->capacity = 0; pc->sz = 0; }
💤排序
这里我们就不得不提快排了,博主写过一篇关于快排的博客,大家可以仔细阅读一下,在这里我们就不再多多赘述了。
//排序 int cmp_s(const void* elem1, const void* elem2) { return strcmp((char*)elem1, (char*)elem2); } void SortContact(Contact* pc) { qsort(pc, pc->sz, sizeof(pc->data[0]), cmp_s); printf("%-10s\t%-5s\t%-10s\t%-5s\t%-10s\n", "姓名", "年龄", "电话", "性别", "地址"); for (int i = 0; i < pc->sz; i++) { printf("%-10s\t%-5d\t%-10s\t%-5s\t%-10s\n", pc->data[i].name, pc->data[i].age, pc->data[i].tele, pc->data[i].sex, pc->data[i].addr); } }
🌠整体代码
💤contact.h
#pragma once //包含头文件 #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<string.h> //宏定义 #define MAX 100//为静态通讯录服务 #define MAX_NAME 20//名字的最大值 #define MAX_SEX 5//性别的最大值 #define MAX_TELE 12//电话的最大值 #define MAX_ADDR 30//地址的最大值 #define DEFAULT_SZ 3//开始通讯录空间大小 #define INC_SZ 2//开辟空间数值大小 //定义枚举,方便使用switch改变 enum OPTION { EXIT,//0 ADD,//1 DEL,//2 SEARCH,//3 MODIFY,//4 SHOW,//5 SORT//6 }; //定义通讯录所包含的元素类型 typedef struct PeoInfo { char name[MAX_NAME]; int age; char sex[MAX_SEX]; char tele[MAX_TELE]; char addr[MAX_ADDR]; }PeoInfo; //静态通讯录 //typedef struct Contact //{ // PeoInfo data[MAX]; // int sz; //}Contact; //动态通讯录 typedef struct Contact { PeoInfo* data;//指向了存放数据的空间 int sz;//记录的当前放的有效元素的个数 int capacity;//通讯录当前的最大容量 }Contact; //函数声明 //初始化通讯录 void InitContact(Contact* pc); //增加联系人 void AddContact(Contact* pc); //显示所有联系人的信息 void ShowContact(const Contact* pc); //删除指定联系人 void DelContact(Contact* pc); //查找指定联系人 void SearchContact(const Contact* pc); //修改指定联系人 void ModifyContact(Contact* pc); //销毁通讯录 void DestroyContact(Contact* pc); //保存通讯录信息到文件 void SaveContact(Contact* pc); //排序 void SortContact(Contact* pc);
💤test.c
#include"contact.h" //续写菜单 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"); } void test() { //定义变量来实现输入 int input = 0; //创建结构体 Contact con; //初始化通讯录 InitContact(&con); //循环 do { //先打印菜单 menu(); printf("请选择->"); scanf("%d", &input); switch (input) { case ADD: AddContact(&con); break; case DEL: DelContact(&con); break; case SEARCH: SearchContact(&con); break; case MODIFY: ModifyContact(&con); break; case SHOW: ShowContact(&con); break; case SORT: SortContact(&con); break; case EXIT: SaveContact(&con); DestroyContact(&con); printf("退出通讯录\n"); break; default: printf("选择错误, 重新选择\n"); break; } } while (input); } //主函数 int main() { test(); return 0; }
💤contact.c
#include"contact.h" //静态版本 //void InitContact(Contact* pc) //{ // assert(pc); // memset(pc->data, 0, sizeof(pc->data)); // pc->sz = 0; //} //静态版本 // //void AddContact(Contact* pc) //{ // assert(pc); // if (pc->sz == MAX) // { // printf("通讯录已满,无法添加\n"); // return; // } // // 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); // // pc->sz++; // printf("成功增加联系人\n"); //} //是否需要扩容(声明函数) int CheckCapacity(Contact* pc); void LoadContact(Contact* pc) { //打开文件 FILE* pf = fopen("contact.dat", "rb"); if (pf == NULL) { perror("LoadContact"); return; } //读文件 PeoInfo tmp = { 0 }; while (fread(&tmp, sizeof(PeoInfo), 1, pf)) { if (0 == CheckCapacity(pc)) return; pc->data[pc->sz] = tmp; pc->sz++; } //关闭文件 fclose(pf); pf = NULL; } //初始化通讯录(动态) void InitContact(Contact* pc) { //判断pc是否为空 assert(pc); //开辟空间 pc->data = (PeoInfo*)malloc(sizeof(PeoInfo)); //判断是否为空 if (pc->data == NULL) { perror("InitContact"); return 0; } //初始化 pc->sz = 0; pc->capacity = DEFAULT_SZ; //文件中保存的信息加载到通讯录中 LoadContact(pc); } //是否需要扩容 int CheckCapacity(Contact* pc) { //判断sz是否等于capacity if (pc->sz == pc->capacity) { //开辟空间 PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo)); //判断 if (ptr == NULL) { perror("CheckCapacity"); return; } else { pc->data = ptr; pc->capacity += INC_SZ; printf("增容成功\n"); return 1; } } } //增加联系人 void AddContact(Contact* pc) { //断言 assert(pc); //如果通讯录为空直接跳出循环 if (CheckCapacity(pc) == 0) { return; } //输入 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); //CheckCapacity(pc); //指向下个元素 pc->sz++; printf("成功增加联系人\n"); } //显示所有联系人的信息 void ShowContact(const Contact* pc) { //断言 assert(pc); int i = 0; //打印列标题 printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址"); //打印数据 for (i = 0; i < pc->sz; i++) { printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr); } } //查找元素下标 static int FindByName(const Contact* pc, char name[]) { int i = 0; for (i = 0; i < pc->sz; i++) { if (strcmp(pc->data[i].name, name) == 0) { return i;//找到了 } } return -1;//找不到 } //删除指定联系人 void DelContact(Contact* pc) { if (pc->sz == 0) { printf("通讯录为空,无法删除\n"); return; } char name[MAX_NAME] = { 0 }; assert(pc); //删除 printf("请输入要删除的人名字:>"); scanf("%s", name); //找到要删除的人 int del = FindByName(pc, name); if (del == -1) { printf("要删除的人不存在\n"); return; } int i = 0; //删除坐标位del的联系人(向前覆盖) for (i = del; i < pc->sz - 1; i++) { pc->data[i] = pc->data[i + 1]; } //要向后退一个元素 pc->sz--; printf("成功删除联系人\n"); } //查找指定联系人 void SearchContact(const Contact* pc) { //断言 assert(pc); char name[MAX_NAME] = { 0 }; printf("请输入要查找人的名字:>"); scanf("%s", name); int pos = FindByName(pc, name); if (pos == -1) printf("要查找的人不存在\n"); else { printf("%-10s\t%-4s\t%-5s\t%-12s\t%-30s\n", "名字", "年龄", "性别", "电话", "地址"); //打印数据 printf("%-10s\t%-4d\t%-5s\t%-12s\t%-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); } } //修改指定联系人 void ModifyContact(Contact* pc) { //断言 assert(pc); char name[MAX_NAME] = { 0 }; printf("请输入要修改人的名字:>"); scanf("%s", name); int pos = FindByName(pc, name); if (pos == -1) printf("要修改的人不存在\n"); else { 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"); } } //销毁通讯录 void DestroyContact(Contact* pc) { //释放 free(pc->data); //置为空和0 pc->data = NULL; pc->capacity = 0; pc->sz = 0; } //保存通讯录信息到文件 void SaveContact(Contact* pc) { FILE* pf = fopen("contact.dat", "wb"); if (pf == NULL) { perror("SaveContact"); return; } //写数据 int i = 0; for (i = 0; i < pc->sz; i++) { fwrite(pc->data + i, sizeof(PeoInfo), 1, pf); } //关闭文件 fclose(pf); pf = NULL; } //排序 int cmp_s(const void* elem1, const void* elem2) { return strcmp((char*)elem1, (char*)elem2); } void SortContact(Contact* pc) { qsort(pc, pc->sz, sizeof(pc->data[0]), cmp_s); printf("%-10s\t%-5s\t%-10s\t%-5s\t%-10s\n", "姓名", "年龄", "电话", "性别", "地址"); for (int i = 0; i < pc->sz; i++) { printf("%-10s\t%-5d\t%-10s\t%-5s\t%-10s\n", pc->data[i].name, pc->data[i].age, pc->data[i].tele, pc->data[i].sex, pc->data[i].addr); } }
🌟结束语
今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小说手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。