首先介绍一下本次动态扩容版本通讯录所要用到的几个动态内存函数。分别是malloc,calloc,realloc和free函数。
malloc函数
malloc函数是一个动态内存开辟的函数,它向内存申请一块连续可用的空间,并返回指向这块空间的指针。
注意事项:
1.如果开辟成功,则返回一个指向开辟好空间的指针。
2.如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要检查是否为NULL!!!
3.返回值的类型是void*,所以malloc函数并不知道开辟空间的类型,所以在具体使用的时候使用者需将返回值强制转换为自己所需要的类型值。
4.如果参数size为0,malloc的行为是标准是未定义的,取决于编译器。
calloc函数
注意事项:
1.该函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0.
2.与函数malloc的区别只在于calloc会在返回地址之前把申请的空间的每个字节初始化为0。
注释:具体要用malloc或者calloc函数取决于使用者需不需将所申请的空间初始化,如果需要从初始化则用calloc,不初始化则用malloc。
realloc函数
其中ptr是要调整的内存地址,size为调整之后新大小。
注意事项:
1.realloc函数返回值为调整之后的内存起始位置。
2.这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
注释:realloc在调整内存空间有两种情况。具体如下图所示:
通讯录(可动态扩容版)代码详解
相对于静态版本的通讯录,动态版本的具体改进有以下几个部分。
1.通讯录结构体:
将原先的struct PeoInfo结构体数组data[]改为所指向动态开辟空间的结构体指针struct PeoInfo *data。新增容量capacity,每当新增的通讯录联系人个数sz达到容量capacity,开始扩容。
struct contact //创建通讯录结构体
{
struct PeoInfo *data; //指向了动态开辟的空间
int sz; //已经放进去信息的个数
int capacity; //容量
};
2.通讯录初始化
静态的初始化使用memset函数将100个struct PeoInfo类型的字节大小赋为0实现通讯录的初始化,动态改进后用malloc动态开辟个DEFAULT_SZ字节的空间,DEFAULT_SZ为宏定义3,即初始容量为3个。注意!!!使用动态内存函数开辟空间,首先需判断所返回的指针是否为NULL。返回NULL则报错,不是则使用,最后一定要记得将所开辟空间free释放掉。
通讯录初始化代码如下:
void InitContact(struct contact*pc) //用malloc动态开辟个DEFAULT_SZ字节的空间;
{
assert(pc);
pc->data = (struct PeoInfo*)malloc(DEFAULT_SZ * sizeof(struct PeoInfo));
if (pc->data == NULL)
{
perror("InitContact()"); //如果malloc开辟空间失败,返回空指针,报错
return;
}
pc->sz = 0;
pc->capacity = DEFAULT_SZ;
}
3.增加通讯录中联系人信息
在原静态版Add_contact()函数的基础上增加检查容量函数check_capacity(),在增加联系人信息之前先判断容量是否已满,是否需要增容。需要增容则调用realloc函数动态新增DEFAULT_ADD*sizeof(struct PeoInfo)字节大小的空间。DEFAULT_ADD为宏定义2,即每次增容2个联系人大小。判断realloc返回值是否为NULL,是则报错,return0,否则增容成功,将realloc动态开辟的空间的首地址赋给pc->data并且容量自加,返回1。
根据check_capacity()函数的返回值判断是否增容成功返回0增容失败,返回1增容成功或者不需要增容。
增加通讯录中联系人信息代码如下:
int check_capacity(struct contact*pc)
{
if (pc->sz == pc->capacity) //容量已满,增容
{
struct PeoInfo*ptr = (struct PeoInfo*)realloc(pc->data, (DEFAULT_SZ + DEFAULT_ADD)*sizeof(struct PeoInfo));
if (ptr != NULL) //动态开辟成功!
{
pc->data = ptr; //将realloc动态开辟的空间的首地址赋给pc->data
pc->capacity += DEFAULT_ADD; //开辟成功,容量自加
printf("增容成功!!!\n");
return 1;
}
else
{
perror("Add_contact()"); //增容失败,报错
return 0;
}
}
else
return 1; //pc->sz未达到容量,不需要增容
}
void Add_contact(struct contact*pc) //增加通讯录中联系人信息
{
assert(pc);
if (0==check_capacity(pc)) //根据check_capacity()函数的返回值判断是否增容成功
//返回0增容失败,返回1增容成功或者不需要增容
{
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);
pc->sz++;
printf("成功增加联系人!\n");
}
4.销毁通讯录
因为我们使用malloc和realloc动态内存函数,使用完后需将所开辟的空间释放掉,所以就不能像之前静态版本一样直接初始化。所以当我们输入input=0时,调用Destroy_contact()函数去释放所申请的空间。具体代码如下:
void Destroy_contact(struct contact*pc)
{
free(pc->data);
pc->data = NULL;
pc->sz = 0;
pc->capacity = 0;
}
代码运行结果图
增容:
结语
通过对于静态版本的改进,对于malloc,calloc,realloc函数的认知更加深刻,实现了通讯录的动态扩容。