一:为什么要有动态内存分配?
创建变量的本质是向内存申请空间。
int a=10;————向内存中申请4个字节的空间来存放10这个整型数据。
int arr[10];————在内存中申请一块连续的空间(40个字节)
int math[30];————如果只有20个人,会有10个整型的空间浪费。
如果一共有30个人,则本数组的大小不够。
思考:如果不够用了,可以变大吗? 如果太多了,可以减小吗? ————答案是不能的。
总结:变量和数组的方式不够灵活,两者空间开辟的大小是固定的,数组在创建时必须指明数组长 度指明后就不可再变化。(数组空间一旦指明大小就不可调整)
但我们对于空间的需求,不仅仅是上述情况,有时候我们需要的空间大小在程序运行时才能知道。那数组的编译时开辟空间的方式就不能满足了。
因此C语言引入了动态内存开辟,让程序员可以自己申请和释放空间,就比较灵活了。
其次使用动态内存开辟还可以维护内存的生命周期。
如果直接创建变量,变量出了其作用域之后会自动销毁,4个字节的空间就与本程序无关了。
如果使用动态内存开辟,什么时候释放空间由程序员自己决定,就比较灵活。
二:malloc和free
1.malloc
C语言提供了一个动态内存开辟的函数
这个函数向内存中申请一块连续可用的空间,并返回指向这块空间的指针
注意:
1.如果开辟成功,则返回一个指向开辟好空间起始位置的指针。
2.如果开辟失败(内存不够),则返回空指针NULL,因此malloc函数的返回值一定要做检查。
3.由于此函数并不知道你申请这块空间用来存储什么类型的数据,因此返回值是void*,具体在使 用时自己决定。
4.如果参数size为0,malloc的行为是标准未定义的,取决于编译器。
2.free
C语言提供了另外一个函数free,专门用来做动态内存的回收。
注意:
1.如果参数ptr指向的空间不是动态开辟的,那free函数的行为是未定义的
2.如果参数ptr是NULL指针,则函数什么事都不会做。
3.malloc和free以及后面的calloc和realloc都声名在stdlib.h头文件中。
4.malloc calloc realloc 一定要与free成对使用(这是一个很好的编程习惯)否则可能引发一些后 果。
5.如果程序退出的时候,即使没有free操作系统也会主动回收动态开辟的空间,但是程序如果不退 出就要小心。
malloc和free的使用案例:
思考:程序的最后使p置为NULL有没有必要?
三:calloc和realloc
1.calloc
C语言还提供了一个函数叫calloc,calloc函数与malloc类似,也用来动态内存开辟。
注意:
1.函数的功能是为num个大小为size的元素开辟一块空间,并且把空间的每个字节初始化为0.
2.与malloc的区别是calloc会在返回地址之前把申请的空间每个字节初始化为全0。
所以如果我们对申请空间的内容要求初始化,那么可以使用calloc很方便的完成任务。
2.realloc
C语言提供了realloc函数用来对动态内存的大小进行调整。
注意:
1.ptr是要调整的内存地址。
2.size是调整之后动态内存的大小。
3.函数的返回值是调整之后的内存起始位置。
4.这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。
5.realloc(NULL,40);等价于malloc(40);
情况一:原有空间之后有足够大的空间。
这种情况下新申请到的空间直接在原有内存之后追加,原来内存空间数据不会变化。
情况二:原有空间之后没有足够大的空间。
这种情况下会在堆空间中另找一个合适的连续空间来使用,这样函数会返回一个新地址。
realloc函数使用案例:
这段代码中不能直接用原来空间地址p来接受realloc的返回值,因为realloc函数也是有可能空间开辟失败的。
四:使用动态内存时常见的错误分析
1.对NULL指针的解引用操作:
本程序中由于开辟的空间过大,内存不足,无法成功开辟,就会返回空指针NULL,形成对空指针的解引用。就会出现问题。
启示:对于动态内存开辟函数的返回值一定要检查是否为空指针。
一道经典的笔试题:
本程序运行之后什么都不打印,最终程序会崩溃,
原因在于str仍然是空指针,strcpy实现的过程中一定会有对str的解引用操作,对空指针解引用是非法的,会报警告。
2.对动态开辟空间的越界访问(非法访问):
非法访问相关的一道笔试题
请问程序的运行结果是什么?
解析:本题目运行结果是未知的,当第一个函数执行结束后p【】数组也会销毁(生命周期结束)
(p数组这块空间已经不属于当前程序了,还是不是hello world就不好确定了)。
再想要找回p数组中的元素(printf函数要通过str去找p数组中的字符,找到一个打印一个) 已经不可能了,(但如果幸运的话,也有可能打印出hello world)
这道笔试题就类似于:
3.对非动态开辟内存使用free释放:
对非动态内存使用free释放系统会崩溃,因为非动态内存使用free函数,free函数的行为是未定义的。
4.使用free释放动态内存开辟的一部分(提前释放):
相关笔试题:
这个程序在结束之前就使用free函数释放了动态内存。
且这个程序在使用free函数之后没有将str置NULL,str的值是不确定的,就会形成野指针。
(找不到之前hello的空间了)。
free函数使用完一定要有一个p=NULL的操作。
5.对同一块动态内存多次释放:
free(p)执行之后,p不再指向动态申请空间内存的地址。
p的指向位置,再次使用free(p)就会有危险。
启示:在使用free(p)之后,一定要有p=NULL的操作。这是一个好的编程习惯。
6.动态开辟内存忘记释放(内存泄漏):
忘记释放不再使用的动态开辟的空间会造成内存泄漏
启示:动态内存开辟的空间最后一定要释放,并且要正确释放。
相关笔试题:
这个程序没有释放开辟的动态内存,造成了内存泄漏。
没有释放,程序本身也找不到它了。
假如有一个服务器上的程序7*24小时运行,每过一星期内存就会耗干,程序挂掉,重启之后又可以运行,一周之后又挂掉————大概率是内存泄露了。
五:柔性数组
柔性数组其实就是指结构体中的最后一个数组成员
这个成员的大小是未知的
本程序结构体中的最后一个数组成员大小未知,是柔性数组成员。
柔性数组的特点:
1.柔性数组成员前必须至少有一个其他成员。
2.sizeof返回时不包含柔性数组的大小。
3.包含柔性数组成员的的结构体要用malloc函数进行内存动态分配,提前适应内存大小。
柔性数组大概实现了变长数组的功能。
柔性数组使用举例:
使用方法二:
柔性数组的优势:
七:C/C++中程序内存区域划分
完.
标签:动态内存,管理,free,内存,数组,空间,函数 From: https://blog.csdn.net/2403_88695928/article/details/143918910