前言:
在前面的学习中,我们写了静态通讯录:c语言实现静态通讯录
一、静态通讯录的弊端:
对于简易版本的通讯录来说,我们是直接指定了通讯录的大小,那么此时我们会发现两个弊端:
弊端1:
如果我们创建的通讯录空间过大,就会浪费空间;如果我们创建的通讯录太小又不够使用 。所以,此时我们就得优化通信录给定空间上的问题,不能再给其指定大小的空间,而是让其空间有灵活性,此时我们以动态增容的方式来给定通讯录的空间,就很好的规避了这一问题!
弊端2:
我们知道程序运行起来我们输入的数据都是保存在内存上的,在简易版本中我们在运行通讯录之后添加的数据,在程序结束的时候都会被清除。当而我们既然要保存联系人的信息,就得做到数据持久化的保存,我们就得将数据保存到硬盘中,也就是文件当中,此时我们可以用文件操作的方式来实现持久化的保存联系人信息。
二、动态增容优化通讯录:
2.1.枚举类型的加入
在前面我们学习了枚举类型的使用,我们在写menu菜单的switch函数时,常常会因为不知道数字对应的是哪一个功能而弄混。
枚举类型中各成员的值为从零开始依次递增,于是我们可以再使用一个枚举类型来使我们的选项对应用户的选择:
enum OPTION { Exit = 0, Add = 1, Del, Search, Modify, Print, sort };
这样一来,我们就可以把case后面的1,2,3等数据换成对应的功能名字。
如case 1:替换为case Add:
2.2.结构体的修改
之前我们创建了两个结构体,第一个存放信息,第二个存放信息的数组和联系人数量,可是第二个存放的人信息的数组规定死了100个元素。所以我们将第二个结构体做一下改动即可:
//静态版本 //typedef struct contact //{ // PeoInfo data[MAX]; // int sz; //}contact; //动态版本 typedef struct Contact { PeoInfo* data;//指向存放人的信息的空间 int sz;//当前已经放的信息的个数 int capacity;//当前通讯录的最大容量 }contact;
这里我们用sz记录联系人数量,用capacity指向当前通讯录的最大容量。在这里我们会发现结构体明显小了很多,因为存放人信息的空间我们还没有创建!
2.3.Initcontact函数的修改
之前的初始化仅仅只是将所有的数据初始化为0,但是动态版本的通讯录还没有开辟联系人的空间。所有我们要在初始化函数里完成这个操作。
//静态版本: //void Initcontact(contact* con) //{ // assert(con); // con->sz = 0; // memset(con->data, 0, sizeof(con->data)); //} //动态版本: void Initcontact(contact* con) { assert(con); //DEFAULT_SZ是宏定义的3,在contact.h有添加! PeoInfo *ptr= (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo)); if (ptr == NULL) { perror("InitContact::calloc"); return; } con->sz = 0; con->capacity = DEFAULT_SZ; con->data = ptr; //这里的Loadcontact函数在下面会提到! LoadContact(con); }
在这里大家就不用自己初始化联系人信息了!因为calloc本身就有这个功能!
2.4.Addcontact函数的修改
之前静态通讯录的添加联系人只需要在不超过容量的基础上添加即可,超过容量就不能操作了。但是动态通讯录在发现容量不足时,要有自动增容的功能,这也是动态通讯录的有点所在!
//静态add //void AddContact(contact* con) //{ // assert(con); // if (con->sz == 99) // { // printf("通讯录已经存满,请联系操作人员"); // return; // } // printf("请输入联系人姓名:>"); // scanf("%s", con->data[con->sz].name); // printf("请输入联系人性别:>"); // scanf("%s",con->data[con->sz].sex); // printf("请输入联系人年龄:>"); // scanf("%d", &(con->data[con->sz].age)); // printf("请输入联系人联系方式:>"); // scanf("%s", con->data[con->sz].tele); // printf("请输入联系人住址:>"); // scanf("%s", con->data[con->sz].addr); // printf("联系人信息添加成功!\n"); // con->sz++; //} // //判断容量是否够用!并且自动增容 void check_capacity(contact* con) { assert(con); if (con->sz == con->capacity) { printf("内存已满,正在扩容——\n"); Sleep(1000); PeoInfo* ptr = (PeoInfo*)realloc(con->data, (con->capacity + INC_SZ) * sizeof(PeoInfo)); if (ptr==NULL) { perror("check_capacity::realloc"); return; } con->capacity += INC_SZ; con->data = ptr; printf("扩容成功\n"); } } //动态add void AddContact(contact* con) { assert(con); check_capacity(con); printf("请输入联系人姓名:>"); scanf("%s", con->data[con->sz].name); printf("请输入联系人性别:>"); scanf("%s", con->data[con->sz].sex); printf("请输入联系人年龄:>"); scanf("%d", &(con->data[con->sz].age)); printf("请输入联系人联系方式:>"); scanf("%s", con->data[con->sz].tele); printf("请输入联系人住址:>"); scanf("%s", con->data[con->sz].addr); printf("联系人信息添加成功!\n"); con->sz++; }
2.5.Desdrycontact函数的添加
在使用完堆区的空间之后,最重要的事情是什么?当然是释放这块空间呀!
void DestroyContact(contact* con) { free(con->data); con->data = NULL; con->sz = 0; con->capacity = 0; con = NULL; }