二、动态版
分析
我们想想,如果要实现动态版,那么在定义结构体的时候,我们就不能定义一个数组了,而应该定义一个指针,这个指针未来是要指向动态开辟的内存的。光定义一个指针够吗?跟静态版一样还需要一个 sz 来计数,还需要什么变量吗?
我们知道起初初始化的时候会开辟一个小一点的动态内存,当这个内存满了之后,就要在此基础上再扩大内存,那么如何判断内存是否满了呢?
我们可以再定义一个变量 max ,该变量记录了当前状态下动态内存最大元素个数,当 sz 与 max 相等的时候,我们便知道内存已满,要扩大动态内存了。
于是,我们可以这样写:
struct contacts { struct Information* data; int sz; int max; };
接下来就是初始化了,在初始化函数里用 malloc 开辟一个动态内存:
void chu_shi(struct contacts* pc) { assert(pc); //这里的3也可以用#define来定义一下,方便修改 pc->data = (struct Information*)malloc(3 * sizeof(struct Information)); pc->max = 3; pc->sz = 0; }
接下来就是添加联系人了,我们知道添加联系人的时候可能会发生内存已满,无法添加的情况,那么在添加前,我们应该进行判断一下,如果内存已满,那么我们就增容,然后继续添加。
我们将增容这个操作封装成一个函数,如下:
int zeng_rong(struct contacts* pc) { //返回一个int方便判断是否增容成功 if (pc->max == pc->sz) { struct Information* ptr = (struct Information*)realloc(pc->data, (pc->max + 2) * sizeof(struct Information)); if (ptr != NULL) { pc->data = ptr; //ptr不为空则赋值 pc->max += 2; //更新一下max的大小 printf("增容成功\n"); //提示一下 return 1; } else { perror("zeng_rong()"); //增容失败则报错 return 0; } } return 1; }
再来看看添加联系人函数:
void Add(struct contacts* pc) { assert(pc); if (0 == zeng_rong(pc)) //增容失败返回0 { return; } 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); printf("添加成功\n"); pc->sz++; }
这样添加函数就完成了,那么接下来的函数需要修改吗,其实后面的函数都不需要修改,因为都没涉及到内存的增长,虽然删减函数会导致内存空出来一点,但是没有修改的必要。
问:那我们这样就修改完了吗?
虽然我们确实实现了通讯录的动态增长,但是我们忘了一个重要点,那就是内存的释放,这点不能忘!
void shi_fang(struct contacts* pc) { free(pc->data); //释放 pc->data = NULL; //置空 pc->max = 0; pc->sz = 0; }
总代码
1.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("***** 0. exit *****\n"); printf("**************************************\n"); } enum Option { EXIT, ADD, DEL, SEARCH, MODIFY, SHOW, SORT }; int main() { int input = 0; struct contacts con; chu_shi(&con); do { menu(); printf("请输入:>"); scanf("%d", &input); switch (input) { case ADD: Add(&con); break; case DEL: Del(&con); break; case SEARCH: Search(&con); break; case MODIFY: Modify(&con); break; case SHOW: Show(&con); break; case SORT: Sort(&con); break; case EXIT: shi_fang(&con); //退出的时候释放 break; default: printf("输入错误\n"); break; } } while (input); return 0; }
2.contacts.h
#pragma once #include<stdio.h> #include<string.h> #include<assert.h> #include<stdlib.h> #define MAX 100 #define MAX_NAME 20 #define MAX_SEX 5 #define MAX_TELE 12 #define MAX_ADDR 30 struct Information { char name[MAX_NAME]; char sex[MAX_SEX]; int age; char tele[MAX_TELE]; char addr[MAX_ADDR]; }; //静态版 //struct contacts //{ // struct Information data[MAX]; // int sz; //}; //动态版 struct contacts { struct Information* data; int sz; int max; }; //初始化通讯录 void chu_shi(struct contacts* pc); //增加联系人 void Add(struct contacts* pc); //删除联系人 void Del(struct contacts* pc); //查找联系人 void Search(const struct contacts* pc); //修改联系人 void Modify(struct contacts* pc); //显示联系人 void Show(const struct contacts* pc); //名字排序 void Sort(struct contacts* pc); //释放空间 void shi_fang(struct contacts* pc);
3.contacts.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"contacts.h" //静态版 //void chu_shi(struct contacts* pc) //{ // assert(pc); // memset(pc->data, 0, MAX * sizeof(struct Information)); // pc->sz = 0; //} void chu_shi(struct contacts* pc) { assert(pc); pc->data = (struct Information*)malloc(3 * sizeof(struct Information)); pc->max = 3; pc->sz = 0; } //静态版 //void Add(struct contacts* 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); // printf("添加成功\n"); // pc->sz++; //} int zeng_rong(struct contacts* pc) { if (pc->max == pc->sz) { struct Information* ptr = (struct Information*)realloc(pc->data, (pc->max + 2) * sizeof(struct Information)); if (ptr != NULL) { pc->data = ptr; pc->max += 2; printf("增容成功\n"); return 1; } else { perror("zeng_rong()"); return 0; } } return 1; } void Add(struct contacts* pc) { assert(pc); if (0 == zeng_rong(pc)) { return; } 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); printf("添加成功\n"); pc->sz++; } int cha_zhao(const struct contacts* pc, char* name) { int i = 0; for (i; i < pc->sz; i++) { if (strcmp(pc->data[i].name, name) == 0) return i; } return -1; } void Del(struct contacts* pc) { char name[20]; printf("请输入要删除的联系人:>"); scanf("%s", name); int m = cha_zhao(pc, name); if (m != -1) { pc->data[m] = pc->data[pc->sz-1]; pc->sz--; printf("删除成功\n"); } else printf("没有该联系人\n"); } void Search(const struct contacts* pc) { char name[20]; printf("请输入要查看的联系人:>"); scanf("%s", name); int m = cha_zhao(pc, name); if (m != -1) { printf("%-20s\t%-6s\t%-4s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "住址"); printf("%-20s\t%-6s\t%-4d\t%-12s\t%-30s\n", pc->data[m].name, pc->data[m].sex, pc->data[m].age, pc->data[m].tele, pc->data[m].addr); } else printf("没有该联系人\n"); } void Modify(struct contacts* pc) { char name[20]; printf("请输入要修改的联系人:>"); scanf("%s", name); int m = cha_zhao(pc, name); if (m != -1) { printf("请输入联系人姓名:>"); scanf("%s", pc->data[m].name); printf("请输入联系人性别:>"); scanf("%s", pc->data[m].sex); printf("请输入联系人年龄:>"); scanf("%d", &(pc->data[m].age)); printf("请输入联系人电话:>"); scanf("%s", pc->data[m].tele); printf("请输入联系人住址:>"); scanf("%s", pc->data[m].addr); printf("添加成功\n"); } else printf("没有该联系人\n"); } void Show(const struct contacts* pc) { int i = 0; printf("%-20s\t%-6s\t%-4s\t%-12s\t%-30s\n", "姓名", "性别", "年龄", "电话", "住址"); for (i; i < pc->sz; i++) { printf("%-20s\t%-6s\t%-4d\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); } } int bi_jiao(const void* e1, const void* e2) { return strcmp(((struct Information*)e1)->name, ((struct Information*)e2)->name); } void Sort(struct contacts* pc) { qsort(pc->data, pc->sz, sizeof(struct Information), bi_jiao); } void shi_fang(struct contacts* pc) { free(pc->data); pc->data = NULL; pc->max = 0; pc->sz = 0; }
总结
与静态版相比,动态版可以根据需求来开辟空间大小,更节省空间,提高代码运行速率。