目录
一.引入枚举
二.动态内存开辟
contact.h
1.初始化
最终代码
2.扩容
最终代码
3.退出自动销毁空间
4.减容
最终代码
三.依姓名排序
通讯录1.0里的 PeoInfo data[MAX] 一次开辟定了内存空间。我放150个人空间不够。只放15个人又太浪费空间。
我们对通讯录1.0进行改造升级。
扩容:方便测试,先给3个内存空间,以后当前联系人数量 == 最大联系人数量时,最大容量增加2个内存空间。
减容:当最大人数比现有人数多5个以上,减容最大人数3个内存空间。
一.引入枚举
test.c 的 switch 语句中的 case 1: case 2: ......我们写代码时很容易忘记这些 1,2......分别代表什么含义。
枚举常量的下标默认从0开始,每次增加1 。我们引入枚举常量。
这样在 case x:这里就能看到想要干什么。
test.c
enum Option
{EXIT,//0ADD, //1DEL, //2SEARCH,MODIFY,SHOW,SORT,DELALL
};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:break;
case DELALL:DelAllContact(&con);break;
case EXIT:printf("退出通讯录\n");break;
default:printf("输入错误\n");break;
}
二.动态内存开辟
我们要改造,PeoInfo data[MAX] 这里就不能是数组了。
contact.h
//静态版本
typedef struct Contact
{PeoInfo data[MAX];//存放人的信息int sz;//当前已经存入联系人的个数
}Contact;
我们要用 malloc 开辟一块空间,把地址存起来。
所以,要把 data 改为 PeoInfo * 类型的指针。由 data 指向开辟的空间。data 指向空间里存放人的信息。
除了 data,sz ,还需要记录当前最大容量。
#include <stdlib.h>#define DEFAULT_SZ 3 //通讯录默认大小
#define INC_SZ 2 //每次增加
#define RED_SZ 3 //每次减少
#define DIFF_SZ_CAPA 5 //最大 - 当前 的差值//动态版本
typedef struct Contact
{PeoInfo* data;//指向存放人信息的空间int sz;//当前已经存入联系人的个数int capacity;//当前通讯录的最大容量
}Contact;
现在data 还没有指向的空间。
1.初始化
contant.c
//静态版本
void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;memset(pc->data, 0, sizeof(pc->data));
}
pc->sz = 0;肯定是不用变的。这里就不能用 memset 了,要用 malloc 开辟内存空间。
我们先开辟空间,再指定通讯录默认大小为3 。这样更合理。
//动态版本
void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;pc->data = (PeoInfo*)malloc(sizeof(PeoInfo) * DEFAULT_SZ);pc->capacity = DEFAULT_SZ;
}
malloc 的参数是要开辟内存的大小(字节)。
所以我们代码中 malloc 参数部分是:一个人的大小 * 通讯录默认人数 = 要开辟字节数
malloc 返回类型 void* 。用 pc 指向的 data 指针接收。data 的类型:PeoInfo * 。所以强制转换。
malloc 是不对开辟的内存空间初始化的。上面这样写,我们要初始化。
所以我用 calloc 。它初始化内存空间的每个字节为0
为 num 个大小为 size 的元素开辟内存空间。
//动态版本
void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;pc->data = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));pc->capacity = DEFAULT_SZ;
}
同时,我们要对开辟的空间进行维护,判断是否开辟成功。
直接用 pc->data 接收不合适
最终代码
//动态版本
void InitContact(Contact* pc)
{assert(pc);pc->sz = 0;PeoInfo* ptr = (PeoInfo*)calloc(DEFAULT_SZ, sizeof(PeoInfo));if (ptr == NULL){perror("InitContact:calloc");return;}pc->data = ptr;pc->capacity = DEFAULT_SZ;
}
2.扩容
contact.c
改为动态开辟版本,里面放了元素,放不下时,要考虑扩容问题。对 Add 更改。
//静态版本
void AddContact(Contact* pc)
{assert(pc);if (pc->sz == MAX){printf("通讯录已满,无法添加\n");return;}//增加一个人的信息printf("请输入名字:");......pc->sz++;
}
动态版本不存在放满的情况,这里不用判断 == MAX
每次添加联系人后进行判断,当最大空间人数 capacity == 当前空间人数 sz 时,扩容。
//动态版本
void AddContact(Contact* pc)
{assert(pc);//增加一个人的信息......if (pc->sz == pc->capacity){//扩容}pc->sz++;
}
用 realloc 对 data 指向的空间调整。扩容的地方分装成函数。
ptr 是要调整的内存地址 size 是调整后的新大小(字节)。返回开辟好新空间的起始地址。
若当最大空间人数 capacity != 当前空间人数 sz 时,不扩容,直接返回。
新大小(字节) = 增加后的人数 * 每个人的大小。
判断是否成功开辟新空间,若是,再将 ptr 传给 pc->data
最终代码
void check_capacity(Contact* pc)
{assert(pc);if (pc->sz == pc->capacity){//扩容PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity + INC_SZ) * sizeof(PeoInfo));if (ptr == NULL){perror("check_capacity:realloc");return;}pc->data = ptr;pc->capacity += INC_SZ;//当前通讯录的最大容量再自增2}
}//动态版本
void AddContact(Contact* pc)
{assert(pc);//增加一个人的信息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].addr);printf("请输入电话:");scanf("%s", pc->data[pc->sz].tele);pc->sz++;check_capacity(pc);printf("\n当前联系人数量:%d\n", pc->sz);printf("当前最大容量:%d\n", pc->capacity);
}
3.退出自动销毁空间
当程序退出时,空间要销毁,free
//test.c
case EXIT:DestroyContact(&con);printf("退出通讯录\n");break;//contact.h 销毁内存空间
void DestroyContact(Contact* pc);//contact.c
void DestroyContact(Contact* pc)
{assert(pc);free(pc->data);pc->data = NULL;pc->capacity = 0;pc->sz = 0;pc = NULL;
}
4.减容
我们还是要判断通讯录是否为空,为空无法删除。
只需在 chack_capacity 函数中加上减容部分,再在 DelContact 函数中加几条语句即可。
最终代码
void check_capacity(Contact* pc)
{assert(pc);if (pc->sz == pc->capacity){//扩容}if ((pc->capacity - pc->sz) > DIFF_SZ_CAPA){//减容PeoInfo* ptr = (PeoInfo*)realloc(pc->data, (pc->capacity - RED_SZ) * sizeof(PeoInfo));if (ptr == NULL){perror("check_capacity:realloc");return;}pc->data = ptr;pc->capacity -= RED_SZ;}
}//动态版本
void DelContact(Contact* pc)
{assert(pc);char name[NAME_MAX] = { 0 };if (pc->sz == 0){printf("通讯录为空,无法删除\n");return;}//查找printf("请输入要删除的人的名字:>");scanf("%s", name);int ret = FindByName(pc, name);if (-1 == ret){printf("要删除的人不存在\n");return;}int i = 0;//删除for (i = ret; i < pc->sz - 1; i++){pc->data[i] = pc->data[i + 1];}pc->sz--;printf("删除成功\n");check_capacity(pc);printf("\n当前联系人数量:%d\n", pc->sz);printf("当前最大容量:%d\n", pc->capacity);
}
三.依姓名排序
用 qsort 函数。(点击 qsort 查看详细说明)
//contact.c
int sortcon_byname(const void* e1, const void* e2)
{return strcmp(((PeoInfo*)e1)->name, ((PeoInfo*)e2)->name);
}void SortContact_ByName(Contact* pc)
{assert(pc);qsort(pc->data, pc->sz, sizeof(PeoInfo), sortcon_byname);printf("排序成功\n");
}//test.c
case SORT:SortContact_ByName(&con);break;
//contact.h
void SortContact_ByName(Contact* pc);
错误写法:
int sortcon_byname(const void* e1, const void* e2)
{return strcmp(((Contact*)e1)->data->name, ((Contact*)e2)->data->name);
}
e1,e2 分别是要比较的两个元素的地址
比较的是人的信息PeoInfo
我们发现,2.0版本的通讯录,程序关闭,不保存这次运行时Add的联系人。
下次运行,按5显示联系人时,是空白。
怎么解决?
期待通讯录3.0