1.malloc和free函数
2.calloc和realloc函数
3.动态内存管理的常见错误
(1)对空指针的解引用操作
(2)对动态开辟空间的越界访问
(3)对非动态开辟内存使用free
(4)使用free释放动态内存空间的一部分
(5)对于同一块动态内存的多次释放
(6)动态开辟内存忘记释放
4.几道关于动态内存管理的经典例题
5.柔性数组
6.C语言里的内存区域划分(附录)
一.malloc和free函数
1.引入:为什么要有动态内存管理
我们已经掌握了两种内存开辟的方式:
但是上述的开辟空间的⽅式有两个特点:
• 空间开辟⼤⼩是固定的。
• 数组在申明的时候,必须指定数组的⻓度,数组空间⼀旦确定了⼤⼩不能调整
但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间⼤⼩在程序运⾏的时候才能知 道,那数组的编译时开辟空间的⽅式就不能满⾜了。 C语⾔引⼊了动态内存开辟,让程序员⾃⼰可以申请和释放空间,就⽐较灵活了
2.malloc函数:
(1)函数原型:
这个函数向内存申请一块连续可用的空间,并返回指向这块空间的指针
注意:
1.这个函数就是用来开辟空间的,且单位为“字节”
2.若开辟成功,就返回一个指向该空间的指针
(开辟成功返回的指针类型为void*,所以我们要使用的话得先将其强制转换)
3.若开辟失败,返回空指针,且返回的空指针NULL无法被使用
4.使用该函数以及后续一系列的free,realloc,calloc都需要包含头文件<stdilb.h>
5.如果size参数为0,malloc的行为标准是未定义的,取决于编译器
(2)使用函数:
3.free函数:
(1)函数原型:
这个函数顾名思义就是来释放动态内存的
注意:
1.如果参数ptr指向空指针,那么free什么事都不用做
2.如果参数ptr指向的空间不是动态内存,那么free的函数的行为是未定义的
3.mclloc函数在申请内存之后一定要释放内存,否则会导致内存泄漏,或者因为重复开辟内存导致占用大量内存空间
(2)函数使用:
二.calloc和realloc函数
1.calloc函数:
(1)函数原型
注意:
1.它可以为num个占用size个字节的元素开辟空间
2.和malloc区别就在于,calloc会把申请的空间全部初始化为0
(2)使用
所以如果我们对申请的内存要求初始化时,就可以很方便地使用calloc函数
2.realloc函数:
(1)函数原型
注意:
1.realloc作为动态内存调整函数,它的出现可以动态内存管理更加灵活
2.ptr表示要调整的内存地址
3.size表示调整后新内存的大小,单位为字节
4.返回值为调整后内存的起始地址
5.这个函数在调整原空间内存的大小的基础上,还会将原来的数据移动到新的内存中
6.realloc调整空间存在以下两种情况:
(1)原空间后有足够大空间
(2)原空间后没有足够大空间
(2)使用
当发生情况1时,要扩展内存直接就在原有空间后追加
当发生情况2时,当原有空间之后没有足够空间时,realloc就会在堆空间上重新找一块空间来存放,这样函数返回的就是一个全新的地址
三.动态内存管理的常见错误
1.对空指针(NULL)的解引用操作:
2.对动态开辟空间的越界访问:
3.对非动态开辟的内存使用free:
4.使⽤free释放⼀块动态开辟内存的⼀部分:
5.对于同一块动态内存的多次释放:
6.动态开辟内存忘记释放(内存泄漏):
切记:动态开辟的内存一定要手动释放,否则会内存泄漏!!!!!
四.几道关于动态内存管理的经典例题
1.题目一:
这串代码运行起来系统会崩溃:因为在过程中GetMemory传的是str本身,也就是一个空指针(但GetMemory确实开辟了一块空间),GetMemory函数并没有改变str指向一个空指针,而是在这个空指针后面又开辟了一块空间(但如果GetMemory返回p,再让str去接收的话也可以实现对程序的完善(这也是更为简单的一种方法)),因此在下面的strcpy中,因为要拷贝字符串,就必然意味着对str这个地址的解引用,二此时str指向的是空指针,也就意味着对空指针进行解引用,而这个操作本身就会导致代码的瘫痪,因此我们若是想要改进代码,使其真正打印hello world,就意味着GetMemory在传参时必须传的时空指针的指针,也就是*str(空指针)的指针,也就是**p,同时,下面的一行代码也应该从空指针改为空指针的指针。
(解释很长,但很高兴能看到你认真读完)
因此改进方式如下:
2.题目二:
这串代码运行出来会是一串乱码,如下:
因为这里p作为一个临时变量,p的生命周期仅仅在GetMemory中,当调用函数后,p指向的栈空间也被返还给操作系统,此时str接受到的返回值也就是一个野指针,也就是这题的主要问题所在,但值得注意的是,虽然p的空间被返还了但str仍旧有可能找到p的地址(在p原所占的空间没来得及被操作系统分配给其他地方时),因此这里打印出乱码也是一个可能情况。
3.题目三:
这个题目就相对比较好处理些,主要应该考虑到的就是这里缺失free释放空间而造成的内存泄漏。
4.题目四:
这里打印出的结果就仅仅是一个“world”,而且还产生了非法访问的问题,具体原因如下:第一点,当代码在执行到free后并没有将str置为空指针NULL,这也就导致了虽然str所指向的空间被释放了,但str仍旧可以找到那一块区域的地址,相应的,str也成为了存着“hello”这个字符串的野指针,当他再一次进入if循环并且通过strcpy打印world时,指向的是hello里h的地址,自然也就使world覆盖了原来的hello,虽然代码可以打印出结果,但代码运行过程中很明显地出现了非法访问与野指针的问题,这也就是这题所考的内容。
五.柔性数组
1.柔性数组的概念:
C99中,结构中的最后⼀个元素允许是未知⼤⼩的数组,这就叫做『柔性数组』成员
2.柔性数组的特点:
举例:
3.柔性数组的使用:
这样柔性数组成员a,相当于获得了100个整型元素的连续空间。
4.柔性数组的优点:
相较于上述方法,还有一种方式同样可以实现:
这两种方式比较过后:
六.C语言里的内存区域划分(附录)
ok,这次的分享就到这,下播。