自学内容网 自学内容网

动态内存管理详解

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,这次的分享就到这,下播。


原文地址:https://blog.csdn.net/2403_87691282/article/details/143965897

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!