在前面我们发布过静态的通讯录的代码实现过程,但是静态版本有一个问题,如果我要存放50个人的信息,那创建的100个人的信息就过于太大了,如果要存放150个人的信息,那么创建的100个人的信息就太小了,所以不能随时随地更改大小,这就是静态版本,那么动态版本就可以改变这一点
动态版本的通讯录:默认的容量可以存放3个人的信息,如果不够了的话,每一次增加2个容量用来存放, 所以就需要将存放联系人信息的这一块空间交给一个指针来维护,而不能再使用数组来维护,然就可以使用我们的内存函数malloc来开辟空间存放联系人的信息,使用realloc函数来扩充这块空间,所以和静态版本对比需要修改的地方有:通讯录的创建、初始化通讯录、增加联系人这几部分
目录
1.创建通讯录
前面提到过要将静态改变为动态需要用指针来维护存放联系人的这块空间,然后还需要一个默认容量
1.1静态版本
头文件:Contact.h
//表示一个联系人的各种信息 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]; //用来存放100个人的信息 int sz; //记录通讯录中有效信息的个数 }Contact;
1.2动态版本
头文件:Contact.h
//表示一个联系人的各种信息 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; //用来存放联系人的信息 int sz; //记录通讯录中有效信息的个数 int capacity; //当前通讯录的一个容量 }Contact;
2.初始化通讯录
在初始化通讯录中,我们需要给通讯录中的data用malloc开辟一块空间,默认可以存放5个人
的信息,如果开辟成功,则将sz置为0,将容量置为5
2.1静态版本
头文件:Contact.h
//初始化通讯录 void InitContact(Contact* pc);
源文件:test.c
int main() { int input = 0; Contact con; //创建通讯录变量 //初始化通讯录 InitContact(&con); //要修改通讯录里面的内容要传递指针 do { menu(); printf("请选择通讯录的功能:>"); scanf("%d", &input); switch (input) { case ADD: //添加联系人 printf("添加联系人\n"); AddContact(&con); //传址调用 break; case DEL: //删除联系人 printf("删除联系人\n"); DelContact(&con); break; case SEARCH: //查找联系人 printf("查找联系人\n"); SearchContact(&con); break; case MODIFY: //修改联系人 printf("修改联系人\n"); ModifyContact(&con); break; case SHOW: //展示 ShowContact(&con); //显示通讯录不需要修改通讯录其实传值调用也可以,但是为了节省空间使用传值调用 break; case SORT: //排序联系人 SortContact(&con); break; case CLEAR: //清空联系人 ClearContact(&con); break; case EXIT: printf("退出通讯录!\n"); break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
源文件:Contact.c
//初始化通讯录 void InitContact(Contact* pc) { pc->sz = 0; memset(pc->data, 0, sizeof(pc->data)); }
2.2动态版本
源文件:test.c
int main() { int input = 0; Contact con; //创建通讯录变量 //初始化通讯录 InitContact(&con); //要修改通讯录里面的内容要传递指针 do { menu(); printf("请选择通讯录的功能:>"); scanf("%d", &input); switch (input) { case ADD: //添加联系人 printf("添加联系人\n"); AddContact(&con); //传址调用 break; case DEL: //删除联系人 printf("删除联系人\n"); DelContact(&con); break; case SEARCH: //查找联系人 printf("查找联系人\n"); SearchContact(&con); break; case MODIFY: //修改联系人 printf("修改联系人\n"); ModifyContact(&con); break; case SHOW: //展示 ShowContact(&con); //显示通讯录不需要修改通讯录其实传值调用也可以,但是为了节省空间使用传值调用 break; case SORT: //排序联系人 SortContact(&con); break; case CLEAR: //清空联系人 ClearContact(&con); break; case EXIT: printf("退出通讯录!\n"); break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
头文件:Contact.h
#define DEFUAULT_SZ 3 //默认容量 #define INC_SZ 2 //每次扩充的容量 #include <string.h> #include <errno.h> //初始化通讯录 void InitContact(Contact* pc);
源文件:Contact.c
//初始化通讯录 void InitContact(Contact* pc) { pc->data = (PeoInfo*)malloc(DEFUAULT_SZ * sizeof(PeoInfo)); if (pc->data == NULL) { printf("通讯录初始化失败:%s\n", strerror(errno)); return; } pc->sz = 0; pc->capacity = DEFUAULT_SZ; }
3.添加联系人
要添加联系人,首先得判断此时此刻通讯录的容量有没有满,如果满了就需要使用realloc来进行增容,所以可以分装一个函数来进行增容判断
3.1静态版本
源文件:test.c
case ADD: //添加联系人 printf("添加联系人\n"); AddContact(&con); //传址调用 break;
头文件:Contact.h
//添加联系人 void AddContact(Contact* pc);
源文件:Contact.c
//添加联系人 void AddContact(Contact* 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++; //每一次添加完之后sz都要++ printf("添加成功\n"); }
3.2动态版本
源文件:test.c
case ADD: //添加联系人 printf("添加联系人\n"); AddContact(&con); //传址调用 break;
头文件:Contact.h
//添加联系人 void AddContact(Contact* pc);
源文件:Contact.c
int CheckCapacity(Contact* pc) { //如果满容量了,那就需要扩充PeoInfo的容量了 if (pc->sz == pc->capacity) { PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo)); if (ptr == NULL) { printf("CheckCapacity:%s\n", strerror(errno)); return 0; } else { pc->data = ptr; //将扩容之后的地址传给data pc->capacity += INC_SZ; //给容量加2 printf("增容成功,当前容量:%d\n", pc->capacity); return 1; } } return 1; } //添加联系人 void AddContact(Contact* pc) { //判断通讯录是否满容量 if (0 == CheckCapacity(pc)) { printf("空间不够,扩容失败\n"); return; } else { 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++; //每一次添加完之后sz都要++ printf("添加成功\n"); } }
4.释放内存
在动态版本中,我们创建的空间都是使用动态内存开辟的,因此,在退出通讯录的时候需要及时的将它们释放掉,防止内存泄漏
4.1动态版本
源文件:test.c
case EXIT: //释放通讯录的空间 DestroyContact(&con); printf("退出通讯录!\n"); break;
头文件:Contact.h
//释放内存 void DestroyContact(Contact* pc);
源文件:Contact.c
//释放通讯录 void DestroyContact(Contact* pc) { free(pc->data); pc->data = NULL; pc->sz = 0; pc->capacity = 0; printf("释放内存....\n"); }
5.完整代码
源文件:test.c
#define _CRT_SECURE_NO_WARNINGS 1 #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("**** 7.Clear ***** 0.Exit ****\n"); printf("**************************************\n"); } enum Option { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT, CLEAR }; int main() { int input = 0; Contact con; //创建通讯录变量 //初始化通讯录 InitContact(&con); //要修改通讯录里面的内容要传递指针 do { menu(); printf("请选择通讯录的功能:>"); scanf("%d", &input); switch (input) { case ADD: //添加联系人 printf("添加联系人\n"); AddContact(&con); //传址调用 break; case DEL: //删除联系人 printf("删除联系人\n"); DelContact(&con); break; case SEARCH: //查找联系人 printf("查找联系人\n"); SearchContact(&con); break; case MODIFY: //修改联系人 printf("修改联系人\n"); ModifyContact(&con); break; case SHOW: //展示 ShowContact(&con); //显示通讯录不需要修改通讯录其实传值调用也可以,但是为了节省空间使用传值调用 break; case SORT: //排序联系人 SortContact(&con); break; case CLEAR: //清空联系人 ClearContact(&con); break; case EXIT: //释放通讯录的空间 DestroyContact(&con); printf("退出通讯录!\n"); break; default: printf("选择错误,请重新选择:>\n"); break; } } while (input); return 0; }
头文件:Contact.h
#pragma once //头文件的包含 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> //大小的定义 #define MAX 100 #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_TELE 12 #define MAX_ADDR 20 #define DEFUAULT_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 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 SortContact(Contact* pc); //清空联系人 void ClearContact(Contact* pc); //释放内存 void DestroyContact(Contact* pc);
源文件:Contact.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "Contact.h" //通讯录的实现模块 //初始化通讯录 void InitContact(Contact* pc) { pc->data = (PeoInfo*)malloc(DEFUAULT_SZ * sizeof(PeoInfo)); if (pc->data == NULL) { printf("通讯录初始化失败:%s\n", strerror(errno)); return; } pc->sz = 0; pc->capacity = DEFUAULT_SZ; } int CheckCapacity(Contact* pc) { //如果满容量了,那就需要扩充PeoInfo的容量了 if (pc->sz == pc->capacity) { PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo)); if (ptr == NULL) { printf("CheckCapacity:%s\n", strerror(errno)); return 0; } else { pc->data = ptr; //将扩容之后的地址传给data pc->capacity += INC_SZ; //给容量加2 printf("增容成功,当前容量:%d\n", pc->capacity); return 1; } } return 1; } //添加联系人 void AddContact(Contact* pc) { //判断通讯录是否满容量 if (0 == CheckCapacity(pc)) { printf("空间不够,扩容失败\n"); return; } else { 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++; //每一次添加完之后sz都要++ printf("添加成功\n"); } } //显示通讯录 void ShowContact(const Contact* pc) { printf("%-10s %-4s %-5s %-12s %-15s\n", "名字", "年龄", "性别", "电话", "地址"); int i = 0; for (i = 0; i < pc->sz; i++) { printf("%-10s %-4d %-5s %-12s %-15s\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; int pos = 0; for (i = 0; i < pc->sz; i++) { //找到所要联系人所在的位置 if (0 == strcmp(pc->data[i].name, name)) { return i; } } return -1; } //删除联系人 void DelContact(Contact* pc) { char name[MAX_NAME] = { 0 }; //判断通讯录是否为空 if (0 == pc->sz) { printf("通讯录为空,无法删除!\n"); return; } printf("请输入你要删除联系人的姓名:>"); scanf("%s", name); int pos = FindByName(pc, name); int i = 0; if (pos == -1) { printf("找不到所要删除的联系人\n"); return; } //进行删除 for (i = pos; i < pc->sz - 1; i++) //判断条件这里所要交换的比总数少一个 { pc->data[i] = pc->data[i + 1]; } pc->sz--; printf("删除成功\n"); } //查找联系人 void SearchContact(const Contact* pc) { char name[MAX_NAME] = { 0 }; if (pc->sz == 0) { printf("通讯录为空,查找不到!"); return; } printf("请输入你要查找联系人的姓名:>"); scanf("%s", name); int pos = FindByName(pc, name); int i = 0; if (pos == -1) { printf("找不到所要查找的联系人\n"); return; } //打印导航栏 printf("%-10s %-4s %-5s %-12s %-30s\n", "姓名", "年龄", "性别", "电话", "地址"); //打印数据 printf("%-10s %-4d %-5s %-12s %-30s\n", pc->data[pos].name, pc->data[pos].age, pc->data[pos].sex, pc->data[pos].tele, pc->data[pos].addr); } //修改联系人 void menu1() { printf("*********************************\n"); printf("******1.姓名*********2.年龄*******\n"); printf("******3.性别*********4.电话*******\n"); printf("*************5.地址**************\n"); printf("*********************************\n"); } void ModifyContact(Contact* pc) { int input = 0; char name[MAX_NAME] = { 0 }; printf("请输入你要修改的联系人姓名:>"); scanf("%s", name); //查找 int pos = FindByName(pc, name); int i = 0; if (pos == -1) { printf("找不到所要查找的联系人\n"); return; } //修改 printf("请输入要修改的具体信息:>\n"); menu1(); scanf("%d", &input); switch (input) { case 1: printf("请输入新的姓名:>"); scanf("%s", pc->data[pos].name); break; case 2: printf("请输入新的年龄:>"); scanf("%d", &pc->data[pos].age); break; case 3: printf("请输入新的性别:>"); scanf("%s", pc->data[pos].sex); break; case 4: printf("请输入新的电话:>"); scanf("%s", pc->data[pos].tele); break; case 5: printf("请输入新的地址:>"); scanf("%s", pc->data[pos].addr); break; default: printf("输入有误,修改失败\n"); return; } printf("修改成功\n"); } void menu2() { printf("***************************\n"); printf("******** 1.NAME **********\n"); printf("******** 2.AGE **********\n"); printf("***************************\n"); } //按照名字排序 int cmp_byname(const void* p1, const void* p2) { return strcmp(((PeoInfo*)p1)->name, ((PeoInfo*)p2)->name); } //按照年龄来排序 int cmp_byage(const void* p1, const void* p2) { return ((PeoInfo*)p1)->age - ((PeoInfo*)p2)->age; } //排序联系人 void SortContact(Contact* pc) { int input = 0; if (pc->sz == 0) { printf("通讯录没有联系人,无法排序!\n"); return; } menu2(); printf("请选择排序的对象:>"); scanf("%d", &input); switch (input) { case 1: qsort(pc, pc->sz, sizeof(PeoInfo), cmp_byname); break; case 2: qsort(pc, pc->sz, sizeof(PeoInfo), cmp_byage); break; } printf("排序成功\n"); //排序成功之后打印一下 ShowContact(pc); } //清空联系人 void ClearContact(Contact* pc) { //初始化通讯录 InitContact(pc); printf("清空成功\n"); //初始完之后再打印 ShowContact(pc); } //释放通讯录 void DestroyContact(Contact* pc) { free(pc->data); pc->data = NULL; pc->sz = 0; pc->capacity = 0; printf("释放内存....\n"); }
关于通讯录的动态版本的代码就在这里了,感谢大家的观看!