通讯录功能预览 :
0. 退出通讯录
- 添加联系人信息
- 删除指定联系人信息
- 查找指定联系人信息
- 修改指定联系人信息
- 显示所有联系人信息
- 以年龄大小排序所有联系人
- 清空所有联系人
为了增加代码的灵活性,定义了一些标识符常量
#define MAX 100 #define NAME_MAX 20 #define SEX_MAX 20 #define TELE_MAX 20 #define ADDR_MAX 20 #define capacity_max 3 #define increase_sz 2
结构体声明:
1.联系人结构体,通过姓名,性别,年龄,电话,住址来描述一个联系人。
struct PeoInfo { char name[NAME_MAX]; char sex[SEX_MAX]; int age; char tele[TELE_MAX]; char addr[ADDR_MAX]; };
2.通讯录结构体,这个结构体的成员变量应该包括一个存储联系人的数组(或者指向存储联系人空间的指针)和一个记录已存储多少联系人的变量。下面是静态和动态的两种不同定义方法。
静态版本:固定可存储联系人的大小
//静态版本 struct Contact { struct PeoInfo date[MAX]; int sz; };
动态版本:除了记录存储联系人个数的变量sz外,还增加了一个记录通讯的录容量的变量capacity。定义了一个指向存储联系人空间的结构体指针。
struct Contact { struct PeoInfo* data;//指向了存储数据的空间 int capacity;//通讯录的容量 int sz;//已经存储联系人的个数 };
通讯录实现的测试逻辑比较简单,所以本篇文章重点讨论用静态和动态不同方法来实现通讯录的功能。逻辑测试在文章最后的test.c中,这里不重点说明。
通讯录初始化:
静态版本:通过memset函数初识化数组中的各个元素。这个函数在之前介绍过,三个参数分别为1.指向要填充的内存块的指针,2.要设置的值,3.改的大小。
void InitContact(struct contact* pc) { //断言 assert(pc != NULL); //初始化 memset(pc->date,0,MAX*sizeof(struct people)); pc->sz = 0; }
动态版本:这里的重点是使用malloc()动态开辟了3个存储联系人的空间。
//通讯录初始化 void InitContact(struct Contact* pc) { //断言 assert(pc != NULL); //动态内存开辟 pc->data = (struct PeoInfo*)malloc(capacity_max*sizeof(struct PeoInfo)); //判断 if (pc->data == NULL) { perror("InitContact"); return; } pc->capacity = capacity_max; pc->sz = 0; }
初始化完成以后,前期的准备工作就完成的差不多了,接下来就是各个通讯录功能的实现。
添加联系人:
静态版本:
这个版本的逻辑相对来说简单一些,只需要判断sz的个数是否在最大存储联系人个数MAX的范围内就可以了。如果sz<MAX,输入联系人的信息,sz的个数++。
//增加联系人 void Contact_add(struct contact* pc) { //断言 assert(pc != NULL); //判断是否还可以添加 if (pc->sz == MAX) { printf("联系人已满,无法添加"); } printf("请输入联系人姓名:>"); scanf("%s",pc->date[pc->sz].name); printf("请输入联系人性别:>"); scanf("%s", pc->date[pc->sz].sex); printf("请输入联系人年龄:>"); scanf("%d", &pc->date[pc->sz].age); printf("请输入联系人电话:>"); scanf("%s", pc->date[pc->sz].tele); printf("请输入联系人地址:>"); scanf("%s", pc->date[pc->sz].addr); pc->sz++; printf("联系人添加成功\n"); }
动态版本:
在这个版本中,我们初始化的容量只够存储3个联系人,这个时候就需要判断一下,如果当sz=capacity的时候,就需要增加存储联系人的容量(可以使用realloc函数),为了逻辑的清晰,把这个过程封装成一个函数。
这个函数只需要在contact.c内部使用,可以用static修饰一下。简单的说明:在不需要增容和增容成功的情况下返回1。如果增容打印错误信息,返回0。
//判断是否需要增容 static int check_capacity(struct Contact* pc) { //联系人的存储数量和容量相等 if (pc->sz == pc->capacity) { //增容 struct PeoInfo* ptr = (struct PeoInfo*)realloc(pc->data,pc->capacity+increase_sz *sizeof(struct PeoInfo)); //判断 if (ptr == NULL) { perror("check_capacity"); return 0; } else { //增容成功 pc->data = ptr; pc->capacity = pc->capacity + increase_sz; printf("增容成功!\n"); return 1; } } else { return 1; } }
动态版本增加联系人:如果check_capacity()的返回值是1,代表不需要增容或者增容成功,录入联系人的消息即可,sz的个数++。
//动态版本添加联系人 void AddContact(struct Contact* pc) { //断言 assert(pc); if (1 == check_capacity(pc)) { //增加人的信息 printf("请输入名字:>"); scanf("%s", pc->data[pc->sz].name); printf("请输入性别:>"); scanf("%s", pc->data[pc->sz].sex); printf("请输入年龄:>"); scanf("%d", &(pc->data[pc->sz].age)); printf("请输入电话:>"); scanf("%s", pc->data[pc->sz].tele); printf("请输入地址:>"); scanf("%s", pc->data[pc->sz].addr); pc->sz++; printf("成功增加联系人\n"); } else { printf("增容出现错误!\n"); } }
两个版本的添加联系人功能就分别完成了。 这里说明一下,因为这两个版本的通讯录是分开来写的,所以在函数名和变量名的部分可能略有差异。
编写好第一个功能后,最好是打印出来看下效果,这样也可以及时的发现错误。
两个版本的打印是一样的。
打印联系人:
//打印通讯录 void ShowContact(const struct Contact* pc) { int i = 0; printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "地址"); for (i = 0; i < pc->sz; i++) { printf("%-20s\t%-5s\t%-5d\t%-12s\t%-30s\n", pc->data[i].name, pc->data[i].sex, pc->data[i].age, pc->data[i].tele, pc->data[i].addr); } }
删除联系人:
要完成这个功能,第一步就是找到这个联系人。后面的查询,修改,也要经历这个过程,可以把找到指定联系人这个动作封装成一个函数。
//查找联系人 static int FindByName(const struct Contact* pc, char name[]) { int i = 0; for (i = 0; i < pc->sz; i++) { if (0 == strcmp(pc->data[i].name, name)) { return i; } } return -1; }
通过strcmp这个函数比较两个字符串是否相同,相同返回这个联系人的下标。
删除:两个版本的删除没有什么区别。
//删除联系人 void DelContact(struct Contact* pc) { char name[NAME_MAX]; printf("请输入要删除人的名字:>"); scanf("%s", name); int ret = FindByName(pc, name); if (ret == -1) { printf("该联系人不存在"); } else { //删除该联系人 for(int j = ret;j<pc->sz-1;j++) { pc->data[j] = pc->data[j + 1]; } pc->sz--; printf("该联系人已删除,不在联系!\n"); } }
接下来的功能两个版本的实现是一样的。
查询:
//查询联系人 SearchContact(const struct Contact* pc) { char name[NAME_MAX]; printf("请输入要查询的名字:>"); scanf("%s", name); int ret = FindByName(pc, name); if (ret == -1) { printf("该联系人不存在\n"); } else { printf("找到了\n"); printf("%-20s\t%-5s\t%-5s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "地址"); printf("%-20s\t%-5s\t%-5d\t%-12s\t%-30s\n", pc->data[ret].name, pc->data[ret].sex, pc->data[ret].age, pc->data[ret].tele, pc->data[ret].addr); } }
修改:
//修改联系人信息 void ModifyContact(struct Contact* pc) { char name[NAME_MAX]; printf("请输入要修改联系人名字:>"); scanf("%s", name); int ret = FindByName(pc, name); if (ret == -1) { printf("该联系人不存在\n"); } else { //修改人的信息 printf("请输入更改后名字:>"); scanf("%s", pc->data[ret].name); printf("请输入更改后的性别:>"); scanf("%s", pc->data[ret].sex); printf("请输入更改后的年龄:>"); scanf("%d", &(pc->data[ret].age)); printf("请输入更改后的电话:>"); scanf("%s", pc->data[ret].tele); printf("请输入更改后的地址:>"); scanf("%s", pc->data[ret].addr); printf("修改成功!\n"); } }
排序:这里我们选择使用qsort()这个库函数根据年龄从小到大进行排序。qsort这个函数在之前的文章中进行过模拟实现。
int CmpAge(const void* e1, const void* e2) { return ((struct PeoInfo*)e1)->age - ((struct PeoInfo*)e2)->age; } //按照年龄排序 void SortContact(const struct Contact* pc) { qsort(pc->data, pc->sz, sizeof(struct PeoInfo), CmpAge); printf("排序成功!\n"); }
清空联系人:
//清空联系人 void EmptyContact(struct Contact* pc) { InitContact(pc); printf("通讯录已经清空\n"); }
退出: 动态版本在退出通讯录时,要记得释放内存空间。
//释放空间 void DestroyContact(struct Contact* pc) { free(pc->data); pc->data = NULL; pc->capacity = 0; pc->sz = 0; }
效果截图:
test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "contacts.h" void menu() { printf("**************************************\n"); printf("***** 1. add 2. del *****\n"); printf("***** 3. search 4. modify *****\n"); printf("***** 5. show 6. sort *****\n"); printf("***** 7. empty 0. exit *****\n"); printf("**************************************\n"); } enum Option { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT, EMPTY }; int main() { //创建动态的通讯录 struct Contact con; //通讯录初始化 InitContact(&con); int input = 0; 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 EMPTY: EmptyContact(&con); break; case EXIT: DestroyContact(&con); printf("退出通讯录\n"); break; default: printf("选项错误,请重新输入:>\n"); break; } } while (input); return 0; }