动态内存管理


前言

hello呀!各位博友,今天我们来一起学习一下动态内存管理,对于这个内容呀咱们可得好好的来学学,因为咱们学习数据结构的时候它可是核心成员呀!好啦,废话不多说,咱们来一起开始学习吧!

一、为什么要有动态内存

在我们现在已知的开辟空间的方式呢有两种,我们以代码的示例来展示给大家看一下在这里插入图片描述
我们可以看到这两种开辟空间的方式都有着两大特点:
1.开辟的空间大小是不固定的;
2.对于数组开辟的空间一开始就必须固定好了,不能调整;

但是在对于我们的内存空间使用的时候,很多遍时候,并不是一成不变的,就如我们的生活一样,每天都有着变化,所以呢,这个时候我们为了更好合理的使用咱们空间,就引入了动态内存管理,可以让我们自己对所需要使用的空间进行申请和释放,是不是感觉非常的方便呢

二、动态内存管理的相关函数

1.malloc和free

首先,咱们先来认识这俩个动态内存管理的函数。

malloc

在C语言中,给我们提供了一系列的函数,这其中就包括了malloc,我们先一起来看看它长什么样子
在这里插入图片描述
malloc这个函数呢,可以向内存申请一块连续可用的空间,并将其地址返还给我们。
我们还可以发现malloc这个函数的返回类型是任意的,那么则说明我们可以按照自己的需求进行申请空间,在malloc的括号后面,则是我们所需要申请的大小以及类型,我们对于malloc这个函数要注意到以下几点事项:
• 如果开辟成功,则返回⼀个指向开辟好空间的指针。
• 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。
• 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃⼰来决定。
• 如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。

还是那句话呀,光说无凭,也不易理解,咱们来一起从代码上来感受一下malloc申请空间
在这里插入图片描述
这样,咱就成功的申请了咱们的空间,并且使用了这个空间进行存储数字,是不是感觉很奇妙呢!

free

世间万物都是对立的,咱们的动态内存管理函数也是如此,我们既然可以申请空间,那么我们肯定也可以进行释放空间。没错,在C语言中,有一个专⻔是⽤来做动态内存的释放和回收的函数,那就是free,和它的名字一样,free,自由,那让我们先来认识一下它吧
在这里插入图片描述
free函数⽤来释放动态开辟的内存。
• 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。
• 如果参数 ptr 是NULL指针,则函数什么事都不做。
malloc和free都声明在 stdlib.h 头⽂件中。

还是那句话光说无凭,咱们一起来看一下代码示例
在这里插入图片描述
就这样,咱们就把我们刚才所申请的空间释放啦!

2.calloc和realloc

接下来,再带大家认识一下这两个动态内存管理的函数。

calloc

首先,咱们来看看这个calloc这个函数,它的功能也是用来动态内存分配的,我们先来老规矩,认识一下它,看看它长什么样子。
在这里插入图片描述
大家仔细看看下面这段话,理解一下函数calloc
• 函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。
• 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

相信大家应该可以发现calloc和malloc的区别,malloc在进行申请空间的时候并不会给其空间进行初始化数值,而calloc就会在申请空间时对其进行空间的初始化赋值,大家一起来看看示例吧!
在这里插入图片描述
很直观,咱们能够发现,calloc申请的空间都被初始化了数值0。所以如果我们对申请的内存空间的内容要求初始化,那么可以很⽅便的使⽤calloc函数来完成任务。

realloc

这是咱们在动态内存管理中学的最后一个函数,它的出现,可谓是锦上添花,realloc函数的出现让动态内存管理更加灵活。有时会我们发现过去申请的空间太⼩了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的使⽤内存,我们⼀定会对内存的⼤⼩做灵活的调整。那 realloc 函数就可以做到对动态开辟内存⼤
⼩的调整。首先,我们先来看看它长什么样子
在这里插入图片描述
同样的大家看一下下面的总结
• ptr 是要调整的内存地址
• size 调整之后新⼤⼩
• 返回值为调整之后的内存起始位置。
• 这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到新的空间。
• realloc在调整内存空间的是存在两种情况:
◦ 情况1:原有空间之后有⾜够⼤的空间
◦ 情况2:原有空间之后没有⾜够⼤的空间

对于以上的几点,我想前两点大家应该可以都理解,我们来一起讲解一下后面俩个点,咱们以代码示例和图像来给大家讲解
在这里插入图片描述
相信通过这个图例,大家应该都可以明白了俩种情况,但是其实还有第三种情况,那就是开辟失败了,所以大家在申请完空间的时候一定要进行判断是否申请成功!

三、常见的动态内存的错误

动态管理中动态二字,就说明了动态内存管理十分的灵活,容易出错,所以当我们在实际使用动态内存管理的知识时,极易出现一些错误,接下来,我们就通过六道例题来给大家展示一下这些错误

1.对NULL指针的解引⽤操作

在这里插入图片描述
不难发现,此处在开辟空间后并没有对其开辟的空间进行检查是否开辟成功就直接进行解引用,这样是不对的

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

在这里插入图片描述
对于这道题,从图中就可以发现,存在的问题是越界访问。

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

此个问题其实只需要记住,我们使用的动态内存管理函数开辟的空间,我们就要使用free来去释放我们开辟的空间,除此以外开辟的空间所释放空间不要把使用我们的函数free。

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

在这里插入图片描述
这个示例的错误,我已在写在图片的注释了,很明显,p指针已经不再是原来的地址了。

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

这个问题其实不是很容易犯,大家只要记住,你申请的空间,你在释放过后就不要去再次释放即可。

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

在这里插入图片描述
忘记释放不再使⽤的动态开辟的空间会造成内存泄漏。
切记:动态开辟的空间⼀定要释放,并且正确释放。

四、柔性数组

也许你从来没有听说过柔性数组(flexible array)这个概念,但是它确实是存在的。
C99 中,结构中的最后⼀个元素允许是未知⼤⼩的数组,这就叫做『柔性数组』成员。先给大家看看吧,具体是什么样子
在这里插入图片描述

1.柔性数组的特点及其使用

• 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员。
• sizeof 返回的这种结构⼤⼩不包括柔性数组的内存。
• 包含柔性数组成员的结构⽤malloc ()函数进⾏内存的动态分配,并且分配的内存应该⼤于结构的⼤⼩,以适应柔性数组的预期⼤⼩。

以前两个特点给大家举个例子
在这里插入图片描述

接着我们来看看最后一个特点,它的内存是怎样进行分配的,我们同样以图为例
在这里插入图片描述
这样柔性数组成员a,相当于获得了100个整型元素的连续空间。

2.柔性数组的优势

上述的 type_a 结构也可以设计为下⾯的结构,也能完成同样的效果
在这里插入图片描述
上述 代码1 和 代码2 可以完成同样的功能,但是 ⽅法1 的实现有两个好处:第⼀个好处是:⽅便内存释放如果我们的代码是在⼀个给别⼈⽤的函数中,你在⾥⾯做了⼆次内存分配,并把整个结构体返回给⽤⼾。⽤⼾调⽤free可以释放结构体,但是⽤⼾并不知道这个结构体内的成员也需要free,所以你不能指望⽤⼾来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存⼀次性分配好了,并返回给⽤⼾⼀个结构体指针,⽤⼾做⼀次free就可以把所有的内存也给释放掉。第⼆个好处是:这样有利于访问速度.连续的内存有益于提⾼访问速度,也有益于减少内存碎⽚。(其实,我个⼈觉得也没多⾼了,反正你跑不了要⽤做偏移量的加法来寻址)

五、总结C/C++中程序内存区域划分

在这里插入图片描述
此处以此张图片即可划分清楚!

总结

嗨呀嗨呀,这么久了,咱们终于把动态内存管理给一起学完啦,是不是感到收货满满呢!此篇文章,咱们一起学习了动态内存管理的函数,以及常见的错误,柔性数组等问题,希望可以给各位博友带来收获。我是编程渝小黔,为您持续带来编程知识文章,我们下期再见~~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值