自学内容网 自学内容网

【C语言】动态内存管理(下)(realloc函数)

前言

动态内存开辟(上)中我向大家介绍了malloc、calloc以及介绍动态内存常见的错误。那么在本文中,我将继续给大家介绍另一个非常重要且实用的动态内存管理的函数——realloc函数,以及再深入探讨一下free函数的使用细节,避免在使用动态内存函数时,由于不及时释放或者时胡乱释放所造成不必要的麻烦出现。

1. realloc

C语言中还有一个用于动态内存开辟的函数——realloc函数,其功能十分的重要。

为什么为会这么说呢?
这就要与realloc函数的功能挂钩了。大家不妨回想一下,无论是malloc或者是calloc申请的动态内存空间一旦申请了之后就无法再调整空间的大小(除非自己设置一小段代码,进行调整)。比如:有时我们发现过去申请的空间太小了,有时候又觉得申请的空间太大了,那为了合理的使用内存,我们一定会对内存的大小做灵活的处理。realloc函数就可以做到对动态内存开辟的空间进行调整。

realloc函数的功能:调整动态开辟后的空间大小

realloc具体细节如下:
realloc
realloc

函数原型:void* realloc (void* ptr, size_t size)

  • ptr是指向要调整的内存起始地址
  • size是表示要内存调整之后总的大小,单位为字节
  • 返回值为调整之后内存空间的起始地址
  • 这个函数调整原内存空间的大小的基础上,还会将原来内存中的数据移动到新的空间中(也就是realloc申请的内存中)。
  • 不过要注意(记住):realloc函数在调整空间时,存在两种情况:
    • 情况1:原有空间之后有足够大的空间
    • 情况2:原有空间之后没有足够大的空间

2. realloc函数在调整空间时的细节

为了方便大家的理解,我用图解的方式再展示一遍realloc在调整空间时的情况。

realloc调整空间大小的情况

2.1 针对情况1(realloc后面有足够的内存空间)

当是情况1的时候,要拓展内存就直接在原有的空间之后直接追加空间,原来空间的数据不会发生改变。

一句话概括:realloc后面的空间足够的话,直接在其后面调整内存大小即可。

2.2 针对情况2(realloc后面没有足够的内存空间)

当是情况2的时候,原有空间之后没有足够多的空间时,拓展的方法是:在堆空间上寻找另一个合适大小的连续空间来使用。这样函数返回的是一个新的内存起始地址。

2.3 realloc函数使用的注意事项

由于上述的两种情况,realloc函数在使用时就得注意一些细节的问题:

#include <stdio.h>
#include <stdlib.h>
int main()
{
 int *ptr = (int*)malloc(100);
 if(ptr != NULL)
 {
 //业务处理
 }
 else
 {
 return 1; 
}
 //扩展容量
 
 //代码1 - 直接将realloc的返回值放到ptr中
ptr = (int*)realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)
 
 //代码2 - 先将realloc函数的返回值放在p中,不为NULL,在放ptr中
 int*p = NULL;
 p = realloc(ptr, 1000);
 if(p != NULL)
{
ptr = p;
 }
 //业务处理
 free(ptr);
 return 0;
}

我们一般使用realloc函数调整空间大小时,不会直接将要调整空间的起始地址作为接收realloc函数的返回值。因为,如果realloc函数在申请申请空间失败之后,会返回一个NULL值,也就说不仅realloc申请失败了就连之前申请的那块空间也找不到了。这就会造成严重的信息丢失的问题。

2.4 realloc的使用实例

题目:加入之前我创建的10个整型的数据空间不够用了,我想扩容到20个整形数据的大小,并插入数据。

int main()
{
int* p = (int*)malloc(10*sizeof(int));
if (p == NULL)
{
perror("malloc fail");
return 1;
}

//向申请空间里面填入1~10的数据
for (int i = 0; i < 10; i++)
{
*(p + i) = i + 1;
}

//到这里,突然我想扩容了,扩大到20个字节的大小,另外在这些数据的后面接着填入11~20的数据
//----------------------------------------
int* ptr = (int*)realloc(p,sizeof(int)*20);//只有强制类型之后我们才能随意操作这块内存空间
if (ptr == NULL)
{
perror("realloc fail");
return 1;
}
p = ptr; //这步得记住

//-----------------------------------------
for (int i = 10; i < 20; i++)
{
*(p + i) = i + 1;
}

for (int i = 0; i < 20; i++)
{
printf("%d ", *(p + i));
}

free(p);
p = NULL;

return 0;
}

上述的一些代码的具体细节需要读者们养成良好的编程习惯。多敲多练。

2.5 realloc函数的补充

realloc的函数原型为void* realloc(void* ptr, size_t size)。可能有些脑洞大开的读者就会想,如果我给形参ptr的值为NULL会发生什么情况。

给realloc形参中的ptr赋值为NULL就相当于malloc。也就是说,

realloc(NULL,40) == malloc(40)

还有就是,realloc函数在调整空间时属于第2种情况的话,其会自动释放之前的那块空间,不需要我们再手动释放了。 这点细节希望读者们牢记。

3.动态内存常见的错误

相信有不少读者在刚接触到动态内存管理时,总是会用出花来,而这些情况往往也是导致程序出现崩溃的罪魁祸首。
那就让我们来看看,有多少种我们常见的动态内存的错误用法。

3.1 对NULL指针的解引用操作

void test()
{
int* p = (int*)malloc(INT_MAX / 4);
*p = 20;//如果p的值为NULL,就会出现问题
free(p);
}

3.2 对动态开辟的空间的越界访问

void test()
{
int i = 0;
int* p = (int*)malloc(10*sizeof(int));
if(NULL == p)
{
exit(EXIT_FAILURE);
}
for(int i = 0;i <= 10;i++)
{
*(p+i) = i;//当i为10的时候就会出现越界访问的情况
}
free(p);
p = NULL;
}

3.3 对非动态开辟内存使用free释放

void test()
{
int a = 10;
int* p = &a;
free(p);//这个就是错误的,因为指针p指向的不是动态内存开辟的空间
}

3.4 使用free释放一块动态开辟内存的一部分

void test()
{
int*p = (int*)malloc(100);
p++;
free(p);//此刻的p不再是指向动态内存的起始地址了
}

3.5 对同一块内存空间多次释放

void test()
{
int *p = (int*)malloc(100);
free(p);
free(p);//重复释放
}

3.6 动态开辟内存忘记释放(内存泄漏)

void test()
{
int* p = malloc(100);
if(NULL != p)
{
*p = 20;
}
}

int main()
{
test();
whlie(1);
}

忘记释放不再使用的动态开辟的空间会造成内存泄漏。
切记:动态开辟的空间一定要及时释放,并且是正确的释放。

4.总结

在本文中,我们学习到了realloc的各种细节,以及动态内存常见的错误。希望读者们以后在写代码时尽量避免这些错误,否则程序就会陷入到水深火热之中。

结合动态内存管理(上)的内容,我们已经清楚的知道了动态内存开辟是怎么一回事了。总而言之,就是玩明白malloc、calloc、realloc、free这几个函数。


原文地址:https://blog.csdn.net/tianxiawushanu/article/details/140584121

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!