目录
前引
静态与动态内存:
1:相较于静态内存,动态内存可以按需分配,取多少用多少,避免空间浪费,比如(静态的创建变量,这种就随便开辟了一块空间,可能用不完):
2:突破栈空间限制,栈空间有限且容易溢出,而动态空间利用堆空间进行存储
3:延长变量生命周期,内部变量在函数返回后任然有效,实现局部变量的全局化使用
后面还给大家整理了常见的错误点,避免了这些坑,动态开辟直接满分!
看到这里,有没有想一睹动态内存真容的冲动!我们一起来了解了解吧!
与之相关的4个重要函数
malloc (申请所需内存) calloc (申请所需内存) realloc (调整内存空间) free (释放开辟的内存空间)
头文件均是:<stdlib.h>
动态分配的重点是这四个函数,我们来一一讲解,千万认真记笔记!
在前面我们先只了解这4个函数的参数、返回值、作用,后面会整理各种做法的原因!
malloc—申请所需内存
学习库函数,我们需要了解它的参数,以及返回值,可以登入下面的网站,搜素想了解的库函数
我们可以看到,它只需要一个参数:字节大小
返回值:地址
比如:
创建了一个 int 类型的指针 p ,指针指向 malloc 开辟空间的起始位置,空间的大小是16个字节
注意:
1:开辟的空间需要注明类型,为了之后指针的移动方式来存储数据
2:malloc 后面的参数可以写成表达式,但是要保证表达式的结果是多少多少字节
对开辟空间有效性的判断
下面我们来看一个例子:
我们可以看到,虽然确实打印成功了,但是编译器提醒了你,如果开辟失败,那么P就是空指针,也就不符合语法了,所以我们做一下改进,这个很重要,如果不加判断,那么可能会发生非法访问 ,因为P是空指针,强制移动P可能指向其余不属于自己的空间
判断的2种方式:(自选一种即可)
第一种,需要头文件:<string.h>以及<errno.h>
第二种:
最后提醒三遍:动态开辟空间的,都需要做判断,防止非法访问
动态开辟空间的,都需要做判断,防止非法访问
动态开辟空间的,都需要做判断,防止非法访问
free内存释放
我们在前引里面说过了,动态开辟可以延长变量的生命周期,那么我们开辟了一块动态空间,一直开辟,一直开辟下去,是不是有效空间会越来越少,所以我们需要再使用完后及时释放开辟的空间
那么我们如何释放开辟的动态空间?
方式:free(指针)
指针置空
比如:
注意:先释放后置空,这两步缺一不可!
calloc—申请所需内存
这个函数的申请空间其实和malloc差不多,至于差距我们后面会细说!先了解这个函数的使用就行了!
它需要2个参数:元素个数 元素类型的大小
怎么样?calloc跟malloc的参数其实意思一样,只是calloc的参数把malloc的参数分解成了2个
比如:
malloc跟calloc这两个函数都是开辟动态内存,那么区别是什么?
malloc与calloc的区别
malloc:开辟空间后,没有初始化,直接返回空间位置的起始地址
calloc:开辟空间后,进行初始化(默认全部初始化为0),然后返回空间位置的起始地址
我们也可以从内存上面看到它们两个的区别:
realloc调整动态空间
顾名思义 ,realloc就是用来调整的,我们再开辟空间时,因为malloc跟calloc开辟的空间是连续的,那么我们如果所需要的内存空间过大,后面不够了咋办?这时候就需要realloc来进行调整
2个参数:调整对象 总共需要的空间大小
返回值:地址
注意:返回的地址再用指针接收时,不能用原来的指针,接受后还是需要判断是否开辟成功,再进行指针指向的托付(后面会在易错点里面说,放心!)
比如:(经过realloc调整的空间可以继续使用,因此注意下面图中的 i 的取值)
下面我们来对realloc进行讨论,相信看完,绝对是炉火纯青了!
realloc调整空间的2种情况
第一种情况:
当调整空间足够大时,那么realloc会在原空间后面连续开辟剩余的空间
第二种情况:
当调整空间不足以支撑剩余的所需空间时,会找其它地方重新开辟总大小的连续空间,并把旧看空间的数据拷贝过来,再将旧空间自动释放,同时返回新空间的起始地址,继续使用就行了
比如:(对比这2幅图,我们用realloc调整后可以继续使用,指针指向的位置还是上一个空间结束的位置)
注意点:
如果realloc调整的对象为空指针NULL,就相当于随便找位置开辟了一块指定大小的空间,类似malloc函数作用,比如:
常见的动态内存错误(重点)
1:对NULL指针的解引用操作(未判断指针的有效性)
只要开辟了动态空间,就需要判断指针的有效性,如果开辟失败,那么这个指针就是空指针,再访问就是非法访问了
2:对动态开辟空间的越界访问
我们开辟的是16字节大小的空间,但是使用时却超出了16字节的范围,这就发生了越界访问
3:对非动态开辟内存使用free释放
我们创建的变量是在栈区的,但是我们却对它进行了内存释放,内存释放释放的是堆区,两者毫不相干
4:使用free释放开辟的动态内存的一部分(就是指针指向发生了改变)
free释放内存的原则:指针指向必须是开辟动态空间的的起始位置
下面几种形式就需要特别注意,因为指针发生改变:
这种直接对p指向改变的需要先记录p的起始位置,空间使用完后,让p回到起始位置,再释放空间
比如:
5:对同一块动态空间多次释放
那么我们可以怎么改呢?直接先置空,这样就没有问题了,因为释放置空后,这个空间就不属于它了,再释放也就等于释放一个不存在的空间,没有影响,比如:
6:动态内存的忘记释放
我们知道动态内存可以延长变量的生命周期,如果开辟了,不释放,那么它就一直在堆区存在,这样堆区内存越来越少,程序就崩了!
比如下面这个代码:
内存会一直一直开辟下去,最终会崩溃!我们一定要记得及时释放,用完就释放 !
动态开辟必须流程
使用malloc或者calloc或者realloc开辟动态空间后,都要判断空间有效性,即指针是否是空指针
最后记得进行及时释放
内存大概分配图
几个经典的笔试题
第一题
大家可以先看上面的原题,找找错误,再看解析:
首先,str与p没有建立真正联系,因为是传值操作
其次,GetMemory函数接收的参数应该是二级指针,应该是对*p开辟内存,因为*p才是一个指针
最后,没有对*p进行判断,也没有进行空间释放, 下面是改正:
第二题
错误点很明显:就是GetMemory中有个数组,str需要指向这个数组,但是这个数组是临时变量,函数调用完销毁后,主要是解决这个数组被销毁的问题
改进:(我这里忘记释放内存了!不好意思啊,大家记得加上!)
第三题
这个问题不大,缺一个指针有效性判断跟空间释放,下面是改进:
标签:malloc,属实,释放,内存,动态内存,空间,开辟,指针 From: https://blog.csdn.net/Dovis5884/article/details/145083370