目录
一.前言
本文在前文通讯录源码(链接:https://blog.csdn.net/qq_74641564/article/details/128741939)的基础上进行优化,以实现通讯录成员的动态存储。
二.为什么要动态存储?
1.动态存储的作用:
动态存储能实现对内存资源更合理的分配与使用
2动态与静态存储的区别:
静态版本中的数据没有动态版本方便灵活的分配内存。
拿静态通讯录来举例的话就很显而易见,假如一个静态通讯录需要存储100个人甚至1000个人的信息,那么提前创建的这些人的数据加起来将会极大的占据内存空间,甚至溢出,然而,如果有几个人就存几个人的数据,那么不就可以避免对内存浪费的情况了吗?
三.动态存储的实现
1.通讯录容量
将原来的成员结构体数组删除,取而代之的是成员结构体指针,同时需要创建一个新的变量来表示每次扩容减容的数量。
typedef struct Contact
{
ContactMember* data;
int sz;
int capacity;
}Contact;
2.初始化通讯录
使用calloc函数申请了一定数量的成员结构体大小的空间,此处创建ptr以防返回NULL指针。
//初始化成员信息
//DEFAULT_SIZE定义为常量
void InitContact(Contact* con)
{
assert(con);
con->sz = 0;
//申请空间
ContactMember* ptr = (ContactMember*)calloc(DEFAULT_SIZE , sizeof(ContactMember));
if (ptr == NULL)
{
perror("InitContact::calloc");
return;
}
con->data = ptr;
con->capacity = DEFAULT_SIZE;
}
3.增加/减少通讯录成员
增加通讯录成员
如果前面所申请的空间被使用完的话,那么就需要对原本申请的空间扩容,以此来存放更多的成员数据,因此在增加通讯录成员前对当前容量进行判断是否需要扩容。
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].Address);
printf("请输入号码:");
scanf("%s", con->data[con->sz].Number);
printf("输入完毕!\n");
con->sz++;
}
判断及实现扩容函数的实现
使用realloc函数对原本的容量扩大,同样,也要对返回的指针进行判断。要是直接返回NULL指针可就要出大问题呢...
//检查增容
void check_capacity(Contact* con)
{
if (con->sz == con->capacity)
{
ContactMember* ptr = (ContactMember*)realloc(con->data, (con->capacity + Capacity) * sizeof(ContactMember));
if (ptr == NULL)
{
perror("check_capacity::realloc");
return;
}
con->data = ptr;
con->capacity += Capacity;
printf("扩容成功!\n");
}
}
减少通讯录成员
与增加相似,但需要在减少成员之后再嵌套判断函数。
void DelContact(Contact* con)
{
assert(con);
if (con->sz == 0)
{
printf("没有可以删除的目标!\n");
return;
}
printf("请输入目标删除人:");
char name[NAME_MAX];
scanf("%s", name);
int ret = FindName(con, name);
if (ret == -1)
{
printf("目标人物不存在!\n");
return;
}
memmove(con->data + ret, con->data + ret + 1, sizeof(con->data[0]) * (con->sz - ret - 1));
printf("删除成功!\n");
con->sz--;
//判断减容
check_del_capacity(con);
}
判断及实现减容函数的实现
与增容函数一样,改变的只有加减号。
//检查减容
void check_del_capacity(Contact* con)
{
if (con->sz <= con->capacity - Capacity && con->capacity >= DEFAULT_SIZE)
{
ContactMember* ptr = (ContactMember*)realloc(con->data, (con->capacity - Capacity) * sizeof(ContactMember));
if (ptr == NULL)
{
perror("check_del_capacity::realloc");
return;
}
con->data = ptr;
con->capacity -= Capacity;
printf("减容成功!\n");
}
}
4.重置通讯录
俗话说,东西有借有还,既然前面申请了一定的空间,那么不需要用的时候就要还回去。重置的时候就要把前面申请的内存都给free(释放)掉,再重新申请原本大小的空间。当然,realloc函数也可以实现,任君选择。
void EmptyContact(Contact* con)
{
assert(con);
//释放前者内存
free(con->data);
con->data = NULL;
//重置
con->sz = 0;
ContactMember* ptr = (ContactMember*)calloc(DEFAULT_SIZE, sizeof(ContactMember));
if (ptr == NULL)
{
perror("InitContact::calloc");
return;
}
con->data = ptr;
con->capacity = DEFAULT_SIZE;
printf("重置成功!\n");
}
5.退出通讯录
退出通讯录不能使用重置通讯录的函数,因为这会再申请一块空间,我们只需要将内存还回去就行了。
//退出通讯录
void DestoryContact(Contact* con)
{
con->capacity = 0;
con->sz = 0;
free(con->data);
con->data = NULL;
con = NULL;
}
四.结语
最后大家别忘了把修改的数据替换哦,让我们一起努力学习向心仪的offer进发把!