目录
通讯录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,//0
ADD, //1
DEL, //2
SEARCH,
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;
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
//动态版本
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
平台声明:以上文章转载于《CSDN》,文章全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,仅作参考。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/HDSTQTW/article/details/146609703