动态内存管理

为什么要动态内存分配

我们较为熟悉的是以下两种存储方式,这种类型的存储方式,空间开辟空间的大小是固定的,但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间⼤⼩在程序运⾏的时候才能知道,那么以上开辟空间的⽅式就不能满⾜了,因此引入动态存储,让程序员可以较为灵活的申请和释放空间。

int a=10;//栈上开辟四个字节的空间
int arr[10]={0};//在栈上开辟40个字节的连续空间

malloc、calloc和realloc的区别

malloc函数的形式如下:

void *malloc(size_t size)

 这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针,1.如果返回成功就返回起始地址,如果失败返回null指针,因此malloc的返回值一定要提前检查。2.函数的返回类型是void*所以开辟的空间由自己所决定。3.如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。

free函数的形式如下:

void free(void*ptr)

此函数是⽤来做动态内存的释放和回收的。此外释放空间后还会记得所指向的位置,因此需要置空这样可以防止出现野指针。malloc和free最好是成对存在的。

realloc函数的形式如下:

void *realloc(void*ptr,size_t size)

 realloc函数对动态存储进行调整,ptr是要调整的内存地址,size调整之后的大小,返回值为调整之后的内存起始位置。

realloc的存储情况比较特殊,情况1:原有空间之后有⾜够⼤的空间。情况2:原有空间之后没有⾜够⼤的空间。

下面我们将通过画图进行解释

realloc函数情况的具体分析

calloc函数的形式如下:

void* calloc (size_t num, size_t size);

此函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全 0

常见的动态内存的错误

对NULL指针的解引⽤操作

 void test()
 {
 int *p = (int *)malloc(INT_MAX/4);//malloc函数开辟失败会返回NULL
 *p = 20;//如果p的值是NULL,就会有问题 
 free(p);
 }

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

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

对⾮动态开辟内存使⽤free释放

void test()
 {
 int a = 10;
 int *p = &a;
 free(p);//err
 }

使⽤free释放⼀块动态开辟内存的⼀部分

 void test()
 {
 int *p = (int *)malloc(100);
 p++;
 free(p);//p不再指向动态内存的起始位置 
 }

对同⼀块动态内存多次释放

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

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

void test()
 {
 int *p = (int *)malloc(100);
 if(NULL != p)
 {
 *p = 20;
 }
 }
 
 int main()
 {
 test();//调用之后应该进行释放
 while(1);
 }

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

动态内存经典笔试题分析

案例一:

void GetMemory(char *p)
 {
 p = (char *)malloc(100);
 }
 void Test(void)
 {
 char *str = NULL;
 GetMemory(str);
 strcpy(str, "hello world");
 printf(str);
 }

 此题有两处错误,下面进行解释1.str中存放的是NULL,p中存放是str传过来的地址,然后malloc进行开辟空间并指向p但是本身的str依然是空指针,因此会出现程序崩溃。2.malloc开辟空间后并没在函数使用后进行释放。

案例二:

char *GetMemory(void)
{
 char p[] = "hello world";
 return p;
}
void Test(void)
{
 char *str = NULL;
 str = GetMemory();
 printf(str);
}

 这个案例典型的返回栈空间地址问题,我们要知道对数组来来说,传回一个地址是会产生错误的。这里函数调用后返回了数组p的地址,但是开辟的空间在函数结束调用后就已经销毁了,所以这个空间是不存在的,地址传回是无用的,这样就会导致错误的发生。

案例三:

void GetMemory(char **p, int num)
 {
 *p = (char *)malloc(num);
 }
 void Test(void)
 {
 char *str = NULL;
 GetMemory(&str, 100);
 strcpy(str, "hello");
 printf(str);
 }

 

图像示意图

 由图我们可以看出str是malloc开辟的空间这块空间并没有进行释放和置为空,导致内存泄漏。

 案例四:

void Test(void)
 {
 char *str = (char *) malloc(100);
 strcpy(str, "hello");
 free(str);
//str=NULL;
 if(str != NULL)
 {
 strcpy(str, "world");
 printf(str);
 }
 }

 这个其实在free后就已经是野指针了,因为释放后空间还给操作系统了,后面就无法访问,那么strcpy就是非法访问。

总结

动态内存分配其实还是很考察对指针指向的理解,其次这个知识点对数据结构的学习有很大的影响。希望大家能重视起来,最后希望各位大佬能进行指正,共同进步。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值