动态内存管理(易懂详解)

 一、前言

  C语言中有很多开辟内存的方式C阶段,用的最多的也就是数组。但是,我们每次用数组时只能设定一个固定的值,开辟一个固定的内存大小,就比如在写通讯录的时候,你定义了一个存放100个人的信息的数组,当我们存入人的信息很少时,就会浪费很大的空间,存入人的信息很多时,空间又不够用。而且此时空间开辟的大小又是固定的,不能修改,很不方便。假如我们想要先开辟一块小的内存,之后不够用时再开辟一块更大的内存,不想用时再将这块空间回收。这就既没有了空间的浪费,又可以让存放很多信息时更方便的开辟内存空间。那该怎么解决呢?这时候就要用到动态内存管理来解决了!

二、动态内存函数的介绍

这里需要讲到四个函数:malloc 、free 、calloc 、realloc   

下面我们对其一一讲解:

1、malloc函数和free函数

(1)malloc函数

首先我们可以看到malloc的参数是size_ szie,表示申请多少个字节的空间,并且会指向这块空间的起始地址。

假如我们申请40个字节的内存空间,那么malloc返回值会指向内存空间的起始地址:

接着我们可以看到malloc返回值类型是void*类型,为什么是这样呢?是因为我们在开辟内存空间时,malloc函数并不知道你要存入内存空间的是什么类型,所以我们在使用malloc函数时,需要先搞清楚我们存入内存空间的变量的类型。

  接下来我们打开编译器,去实操认识malloc函数:

使用malloc函数需要包括对应的头文件#inlcude<stdlib.h>。十个整型需要四十个字节,整型是int,所以我们需要将malloc返回值强制转化为int*存放到int*p中

这时候我们就已经申请了40个字节大小的内存空间了。

我们再看malloc函数的返回值:

        

 如果malloc函数申请内存成功,则返回申请到的空间的起始地址

 如果申请失败,则返回NULL指针

综上,malloc函数是不一定会申请成功的,那么我们怎么才能知道它是否申请成功或者失败了呢?我们用perror函数来查看malloc的申请错误原因,当perror收到NULL时就会自动打印出malloc函数的错误原因。所以,以防万一,我们都需要用perror函数来检查malloc函数是否申请成功。如下:

我举个申请失败的例子:

这里的INT_MAX,是整型最大值,这里乘4只是为了让它更大。我们调试可知:

Not enough space,没有足够的空间。这也告诉我们用malloc函数时检查其是否申请成功的一个重要性。

既然我们在需要用空间时可以申请内存空间,那么当我们不用时也可以回收这个空间,C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的。

在讲free函数之前,我们先用一下我们申请的内存空间:

我们将0~9这十个数字存放到我们刚申请的十个空间内。方便之后free函数讲解

在这里我讲解一下*(p+1)=i 这步,也许有的小伙伴们会有疑惑,为什么要这样呢?

我们上面已经讲了,如果malloc函数申请空间成功,则返回这片空间的起始地址,我们存放到p中,p存放的就是这片空间的起始地址。我们p+1 ,那么就会访问第i个地址,在对其解引用,存放i。打印出结果为:

那malloc函数申请的空间,是怎样释放的呢?

(2)free函数

1、free释放——主动释放

2、程序退出后,malloc申请的空间,也会被操作系统回收的——被动回收

正常情况下,谁申请的空间,谁去释放 ,万一我们忘记释放,也会被别人释放,这是一种较为危险的操作。所以我们最好自己free主动释放。

接下来我们来认识free函数:

free函数参数是void*的一个指针,返回类型为void,也就是说这个函数不需要返回。它来释放动态开辟的内存,并且只能释放动态开辟的内存,ptr就是我们要释放的那块空间的起始地址。

我们将我们刚刚开辟的内存空间释放并调试查看p:

结果我放在这里,对比看出,释放前后p中的地址却没有变化,这是为什么呢?

因为free释放的内存空间的大小,p中还时会有地址存放,那p有地址却没内存空间,那么p就成为了一个野指针,一个具有危险性的野指针。

所以我们一般free之后,我们会将p赋值成空指针。如下:

至此,动态内存开辟函数malloc与动态内存释放函数free就讲完了。

2、calloc函数

C语言还提供了一个函数交calloc,calloc函数也用来动态内存分配。原型如下:

calloc函数比malloc函数多了一个参数size_t num,是我们需要申请num个size大小的空间。

calloc申请空间成功也会返回空间的起始地址,申请失败也会返回NULL指针

calloc函数与malloc函数的区别:

看到这个我们总觉得和malloc函数没什么差别,无非malloc需要我们自己算,而calloc不需要

比如:

让人觉得只是少了一个算的步骤,除此之外没什么区别。其实不然,calloc函数申请好空间之后,会将空间初始化为0,malloc不会,这就是它们的区别。

我们打开编译器来看一下它们的区别:

malloc:

calloc:

以上我们就可以看出,malloc函数不会将申请的空间初始化,而calloc函数会。

有人也会疑惑,那究竟应该用哪个呢?

取决于你,如果你申请的空间需要初始化,你就可以选择用calloc,如果不需要,那你就可以选择malloc

3、realloc函数

realloc函数的出现让动态内存管理更加灵活,有时我们发现过去申请的空间太小了,有时候又觉得过大了,那为了合理的使用内存,我们一定会对内存的大小进行调整。那realloc函数就可以做到。

函数原型如下:

ptr是要调整的内存的起始地址,size是调整之后的新大小。

并且这个函数在调整原内存空间大小的基础上,将原来内存中的数据移动到新的空间。

既然内存已经被调整,那它的起始地址也会改变,所以realloc函数的返回值是调整之后内存的起始位置。如果失败的话仍然会返回NULL指针。但是这里有个注意的地方:返回NULL指针不就把原本来的内存空间存放的信息丢失了吗?

我们就先来打开编译器去认识realloc函数应该怎么使用:

当我想要调整空间为20个整型的空间时,使用realloc函数,需要调整的内存的起始地址是p。

我们将调整好的起始地址放入pf中,是因为我用原来的p接收,当realloc开辟失败的时候,会返回NULL指针,p就会变成NULL指针,这样会将原来内存空间存放的数据丢失。所以为了避免这种情况,我们先用另外一个指针pf接收,进而判断pf是否为空指针,如果不是,那就说明realloc函数开辟内存空间成功了,我们便可以再将pf赋给p,继续用p来完成。

那么realloc他是怎么调整内存空间的呢?

realloc调整内存空间是存在两种情况:

情况一:原有空间之后有足够大的空间。

第一种很好理解,后面如果有足够的空间的话,我们直接再开辟内存空间就好了。

情况二:原有空间之后可能被占有了,不能直接增加

如果空间之后被占有了,就如黄色方框部分,使得realloc在调整内存空间时发现没有足够的内存空间可以用。这时,realloc函数会找一块新的,足够的空间,一次性开辟足够需要的空间。

并且,找到新的空间之后:

1、realloc函数会将原来旧空间中的数据,拷贝到新的空间

2、会将旧空间释放掉

3、realloc函数会返回新的空间的地址

  • 16
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值