动态内存管理及相关经典笔试好题

目录

1.动态内存管理的原因:

1.free:

2.malloc:

2.2realloc:

2.2calloc:

3.经典笔试题分析

一:栈空间地址及泄露问题

二:返回栈空间地址问题

三:内存泄漏问题

四:柔性数组:


1.动态内存管理的原因:
int i = 20;

char arr[30]={0};

上述开辟空间的方式有两个特点:

1.大小是已知的固定的。

2.数组空间开辟,必须指定数组长度,一旦空间开辟成功大小不可改变。

上述定义的变量都是大小已知的,但在我们写代码时,大多数情况下我们具体需要的变量的大小是不确定的。

所有C语言引入动态内存开辟使我们写代码对空间的利用更加合理灵活。


2.1free和malloc

1.free:

free是专门用来释放动态内存空间的,原型如下

void free(void*p);

1.如果free的参数指向的空间不是动态开辟的,则free的行为是未定义的,由编译器决定。

2.参数是NULL,则free函数什么也不做。

3.free函数包含在<stdlib.h>头文件中。

2.malloc:
void*malloc(size_t size);

malloc向内存申请一块连续可用的的空间,并且返回一个指向这个空间的指针。函数包含在<stdlib.h>头文件中。

1.如果这块空间开辟成功,返回一个指针

2.如果开辟失败则返回一个空指针

3.为什么返回void*,因为malloc并不清楚使用者会开辟什么类型的空间,具体类型由使用者自己决定。

如果size为0,也就开辟空间为零,具体malloc函数会做什么是未知的,取决于编译器。

malloc与free的应用:

//malloc,realloc,calloc的使用都要包含头文件<stdlib.h>
#include<stdlib.h>
#include<errno.h>        //perror的头文件
int main()
{
   int*p=(int*)malloc(10*sizeof(int));//开辟40字节,(int*)强制转化指针类型
   if(p==NULL)
    {
       perror("malloc");//使用perror来打印错误信息
       return 1;//如果开辟失败返回空指针,则直接结束程序返回1
    } 
   int i = 0;
   for(i=0;i<10;i++)
    {
        *(p+i)=i;
    }
    //打印
   for(i=0;i<10;i++)
    {
        printf("%d",*(p+i));
    }
   free(p);
    p=NULL;//将p置为空指针,防止其成为野指针
   return 0;
}  

2.2realloc:


realloc函数出现使动态内存管理更加灵活,有时回会感觉开辟的空间太小,有时又觉得开辟空间太大,这是realloc函数就起作用了。realloc函数可以调整开辟后的动态空间的大小。

void*realloc(void*ptr,size_t size);

注意:

1.ptr是要调整空间的内存地址

2.size是调整后的大小

3.调整原来大小的基础上,会把新调整后的空间整体移到新的满足大小的空间

由于第三条的原因,realloc开辟空间有两种可能:

一:向后开辟空间时,后面的空间不够用,这时需要另起一个新的足够的空间开辟,返回的也是不同于原来的旧空间的指针。

二:向后开辟空间,后面有足够的空间去开辟,不需要另起一个新的空间,返回的也是指向原来地址的指针。

如图1(情况一):

图2(情况二)

针对以上两种情况,所以在使用realloc后要判断其返回的否为空指针,如果开辟空间失败则返回空指针。

下面是对realloc使用的用例:

2.2calloc:

C语言提供一个函数可以为num个大小为size字节的元素开辟空间,并把每个字节初始化为0。

calloc与malloc的区别为是否会把每个字节初始化为0

用例:

3.经典笔试题分析
一:栈空间地址及泄露问题

请问这个代码是否能够打印hehe

分析:这个代码有三个错误,分别在55行,56行,48行

1.55行GetMemory函数采用传值的方式,无法将malloc开辟的空间返回给str中,

结束后str仍然为空指针。

因为str为一级指针,p也为一级指针,所以p不能接收str,所以str传进值为0

2.56行strcpy直接对str空指针解引用操作会使程序崩溃。

3.GetMemory中开辟的空间在程序中没有释放。导致内存泄漏

知晓错误后如何改进呢,下面是修改后的代码。

将p改为二级指针,第75行不要忘记释放开辟后的内存空间。

二:返回栈空间地址问题

为什么打印的不是hello world?

一步步分析:程序进入Test后,有个指针存放GetMemory的返回指针。

但是函数中的变量是储存在栈区,一旦函数调用结束,函数占用的空间被回收

此时p中存放的字符串空间已被回收,返回的指针指向的地址已经不存在,

此时再用str来接收,str存放的地址不可被读取,str成为野指针,所以打印的是随机值。

那么如何修改这段代码呢,应该可以在char p[]前面加上static修饰,使其变成静态变量,不会随着函数调用结束而结束,它的生命周期随整个程序结束而结束。

三:内存泄漏问题

乍一看这个代码属实没啥问题,但是最后它漏掉一个细节,在Test函数末尾没有释放malloc函数开辟的空间,并且应该在将str置为空指针。

可以在113行加上free(str);和str=NULL;最好在110行判断GetMemory返回的是不是空指针。

这样一来就没什么问题了 。

四:柔性数组:

 柔性数组这个概念可能平常听到的比较少,但它确实存在。在C99中允许存在最后一个成员数组大小是未知的的结构体。这个数组就叫柔性数组。

struct st 
{
    int i;
    char arr[];
};

特点:

1.柔性数组前面至少有一个其他成员变量。比如上面代码的int i;

2.sizeof计算的大小不包含柔性数组的大小。

3.对包含柔性数组的结构体用malloc函数开辟空间,并且开辟的空间应该大于结构体大小,多出空间以便分配给结构体。

柔性数组开辟空间:

这里对柔性数组开辟100个字节空间并赋值打印。

这样柔性数组成员arr获得了连续的空间。

还有另外一种开辟空间的方式:分别开辟结构体与柔性数组的大小

方式一和方式二都可以完成对柔性数组空间的开辟,但哪一种更好一点呢?

方式一更好一点,

1.如果方式二这个代码给别人使用,别人可能不知道除了给结构体释放空间外,还要给结构体成员变量再释放一次,很有可能造成内存泄漏

2.方式一连续开辟的空间有利提高访问速度,减少碎片空间。

  • 26
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值