通讯录无非就是实现以下功能:
1.前言
首先要知道一个人要包含哪些信息,这里就以(姓名,年龄,性别,电话号码,地址)为例,这些信息中,年龄和其他四项是不同的数据类型,其他四项可以使用cha类型的数组来进行表示,所以可以将这五种信息放在一个结构体里,声明一个名为“联系人”的结构体:
typedef struct PeoInfo { char name[MAX_NAME]; int age; char sex[MAX_SEX]; char tele[MAX_TELE]; char addr[MAX_ADDR]; }PeoInfo;
注意:这里数组内的常量最好是定义成宏,这样后续修改起来就很方便了。
这里typedef类型重命名一下比较方便。
当然这只是构建了一个联系人需要包含的信息,但是通讯录中应该包含不止一个人的信息,所以还需要创建一个联系人类型的数组:
PeoInfo data[100];这里就假设最多只能存下100个联系人。
但是这样定义是否有一些缺陷呢?那就是假设我们存储了100个人,还想接着存储联系人,但是由于此时并没有变量来记录通讯录中的联系人的个数,就会产生容量不足的问题。所以,需要一个变量来记录存储的联系人的个数。那我们不妨将这两部分放到结构体里:定义一个通讯录结构体。
typedef struct Contact { PeoInfo data[MAX]; int sz; }Contact;
现在通讯录定义好了,那么sz此时是默认的值,这个值是不可控的,所以需要设计一个初始化函数,对创建的通讯录变量中的内容置为0:
Contact Con;//定义一个通讯录变量 void InitCon(Contact* pc)//初始化 { assert(pc); memset(pc->data, 0, sizeof(pc->data)); pc->sz = 0; }
注意:这里函数的参数部分最好是设计成结构体指针类型,比较节约内存资源,这里也是在前面讲过的内容了。后面的关于通讯录的相关函数的参数部分都设计成了结构体指针类型。
初始化之后,就可以开始设计对通讯录进行操作的函数了。
2.增加联系人
增加联系人函数:
结构体在经过初始化之后,sz的值变成了0,而0刚好可以作为通讯录中的下一个元素的下标。此时通讯录中没有联系人,所以第一个联系人的下标就是sz,后续推理也是成立的。
注意:
- 函数参数是结构体指针类型,此时一定最好要判断该指针是否为空,这是一个良好的习惯。
- 增加联系人之后,一定要更新sz变量。
void AddCon(Contact* pc)//添加联系人 { assert(pc);//pc不能为空 printf("请输入你要添加的人的姓名:\n"); scanf("%s", pc->data[pc->sz].name); printf("请输入你要添加的人的年龄:\n"); scanf("%d", &(pc->data[pc->sz].age)); printf("请输入你要添加的人的性别:\n"); scanf("%s", pc->data[pc->sz].sex); printf("请输入你要添加的人的电话:\n"); scanf("%s", pc->data[pc->sz].tele); printf("请输入你要添加的人的住址:\n"); scanf("%s", pc->data[pc->sz].addr); printf("添加成功!\n"); pc->sz++; }
根据姓名查找函数的设计:FindByName函数:pos可以先设置初始值为-1,假如查找到了对应的联系人,就将下标赋值给pos变量,并返回pos,最后假如没找到该联系人,也直接返回pos,比较方便。
int FindByName(const Contact* pc, char* name) { assert(pc); int pos = -1; for (int i = 0; i < pc->sz; i++) { if (strcmp(pc->data[i].name, name) == 0) { pos = i; break; } } return pos; }
3.删除联系人
删除联系人函数:
同理的,这里也要判断pc指针是否为空,同时在设计删除联系人函数的时候,也可以加一个联系人个数是否为0的判断。
我这里是按着名字来查找的联系人,当然也可以用其他信息来进行查找。
void DeleCon(Contact* pc)//删除联系人 { assert(pc); assert(pc->sz); printf("请输入你要删除的人的姓名:\n"); char name[MAX_NAME]; scanf("%s", name); int pos = FindByName(pc, name); if (pos == -1) { printf("该人不存在!\n"); return; } else { memmove(pc->data + pos, pc->data + pos + 1, sizeof(PeoInfo) * (pc->sz - pos - 1)); } pc->sz--; }
注意:这里的按着名字查找封装成一个函数是比较高效的,后续查找联系人的时候也会用到的。
4.查找联系人
查找联系人函数:(这里就可以调用FindBbyName函数,更高效)
void FindCon(const Contact* pc)//查找联系人 { assert(pc); assert(pc->sz); printf("请输入你要查找的人的姓名:\n"); char tmp[MAX_NAME] = { 0 }; scanf("%s", tmp); int pos = FindByName(pc, tmp); if (pos == -1) { printf("要查找的人不存在!\n"); return; } else { printf("这是查找到的联系人的信息:\n"); printf("%-10s\t%-5s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址"); printf("%-10s\t%-5d\t%-5s\t%-12s\t%-20s", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); } printf("\n"); }
这里要注意以下这里的printf打印的细节,就以 printf("%-10s\t%-5s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址");为例,后面的“姓名”,“年龄”等这些内容,实际上都是字符串,都要以%s的形式打印,要想控制好格式,可以在%和S之间,加字段宽度(控制字段宽度),同时加上一个减号(让内容左对齐),并且在一个信息打印之后加一个'\t'也能更美观,配合换行的使用,即可变得比较美观。
5.修改联系人
修改联系人函数:
同样的这里要修改联系人,首先要查找到要修改的联系人的下标,这里也可以复用FindByName函数进行姓名查找。
void ModiCon(Contact* pc)//修改联系人 { assert(pc); assert(pc->sz); printf("请输入你要修改的人的姓名:\n"); char tmp[MAX_NAME] = { 0 }; scanf("%s", tmp); int pos = FindByName(pc, tmp); if (pos == -1) { printf("要修改的人不存在!\n"); return; } else { printf("请输入你要修改的人的姓名:\n"); scanf("%s", pc->data[pos].name); printf("请输入你要修改的人的年龄:\n"); scanf("%d", &(pc->data[pos].age)); printf("请输入你要修改的人的性别:\n"); scanf("%s", pc->data[pos].sex); printf("请输入你要修改的人的电话:\n"); scanf("%s", pc->data[pos].tele); printf("请输入你要修改的人的住址:\n"); scanf("%s", pc->data[pos].addr); printf("修改成功!\n"); } }
6.显示联系人
显示通讯录函数:
这里在显示通讯录的人的时候,可以模仿一下前面的printf的使用方法,显示的时候使用 \t等符号,展示的比较清楚。
void ShowCon(const Contact* pc)//打印联系人 { assert(pc); printf("\n%-10s\t%-5s\t%-5s\t%-12s\t%-20s\n", "姓名", "年龄", "性别", "电话", "地址"); for (int i = 0; i < pc->sz; i++) { printf("%-10s\t%-5d\t%-5s\t%-12s\t%-20s\n", pc->data[i].name, pc->data[i].age, pc->data[i].sex, pc->data[i].tele, pc->data[i].addr); } }
7.排序联系人
排序联系人函数:
在对联系人进行排序的时候,我们可以自己制定排序的标准,例如:按着姓名排序呢?还是按着年龄来排序。这里就可以使用我们之前讲解过的qsort函数了。(qsort函数忘记了就去看看指针进阶里的文章里对qsort函数的讲解)qsort函数的最后一个参数是一个规定排序方法的函数名。这里针对姓名和年龄,可以写出两种关于排序方式的函数:
通过姓名排序:
int compare_name(const void* p1, const void* p2) { assert(p1); assert(p2); while (*(char*)p1 == *(char*)p2) { if (*(char*)p1 == '\0') return 0; p1 = (char*)p1 + 1; p2 = (char*)p2 + 1; } return (*(char*)p1) - (*(char*)p2); }
通过年龄排序:
int compare_age(const void* p1, const void* p2) { assert(p1); assert(p2); return (*(int*)p1) - (*(int*)p2); }
排序函数便可以这样设计:
void SortCon(Contact* pc)//对通讯录进行排序 { assert(pc); assert(pc->sz); int input = 0; printf("1.姓名\n"); printf("2.年龄\n"); printf("请输入排序方式:\n"); scanf("%d", &input); if (input == 1) { qsort(pc->data,pc->sz,sizeof(PeoInfo),compare_name); } else { qsort(pc->data,pc->sz,sizeof(PeoInfo),compare_age); } }
这里就是简单调用的一下qsort函数,自己设计了一下比较函数。
8.完结
通讯录(静态版)的全部内容就到这里啦,若有不足,欢迎评论区指正,下期见!