本文主要以malloc的动态内存创建和释放原理进行分析。
1. 动态内存
首先搞清楚动态内存的原理。
什么是动态内存?
动态内存一般来说就是堆内存。我们知道栈内存一般用于存储类型变量,而堆内存用于存放类型变量的值。栈中的变量指向堆中的变量值。因为变量一般是事先定义在程序中被编译到文件中的,而变量的值数据一般是动态赋予的。
动态内存一般作为临时变量的存储空间,比如临时变量、形参、结构体变量交换时的临时变量空间、链表结点的创建存储等。
2. 指针内存模型
3. 动态内存创建与释放
#include <malloc.h>
void main(){
int a;
int *p = (int*)malloc(sizeof(int));
printf("%d\n",p); // 输出地址值8337448,表明p有指向的地址。
printf("%d\n",*p); // 输出-842150451,此数表示未初始化值。
*p = 2; // 此时,值域空间已经存在可向空间中赋值。
a = *p;
free(p);
p = NULL;
}
流程解析:
int *p = (int*)malloc(sizeof(int));
-842150451,此数表示未初始化值。
*p = 2;
在堆内存中放值。
free(p);
内存释放,及时回收内存,防止内存占用。
p = NULL;
解除地址的指向关系,防止p变为“野指针”。
4. 内存释放的时机
栈内存:栈内存在离开变量作用域时系统自动释放栈内存。
堆内存:程序执行完后会自动释放该程序占用的堆内存,但防止空间占用应在堆内存无用时手动释放。
函数体内的局部变量在函数结束时自动消亡。很多人误以为p指向的内存也会自动消亡。理由是p 是局部的指针变量,它消亡的时候会让它所指的动态内存一起消亡。这是错误的!指针变量在栈内存中,随离开作用域消亡,指针指向的内存空间在堆内存中,并不会离开作用域而消亡。
(1)指针消亡了,并不表示它所指的内存会被自动释放。
(2)内存被释放了,并不表示指针会消亡或者成了NULL 指针。
如果程序终止了运行,一切指针都会消亡,动态内存会被操作系统回收。
5. free()释放内存
- 就算没有free(),main()结束后也是会自动释放malloc()的内存的,这里监控者是操作系统,设计严谨的操作系统会登记每一块给每一个应用程序分配的内存,这使得它能够在应用程序本身失控的情况下仍然做到有效地回收内存。你可以试一下在TaskManager里强行结束你的程序,这样显然是没有执行程序自身的free()操作的,但内存并没有发生泄漏。
- free()的用处在于实时回收内存。如果你的程序很简单,那么你不写free()也没关系,在你的程序结束之前你不会用掉很多内存,不会降低系统性能;而你的程序结束之后,操作系统会替你完成这个工作。但你开始开发大型程序之后就会发现,不写free()的后果是很严重的。很可能你在程序中要重复10k次分配10M的内存,如果每次使用完内存后都用free()释放,你的程序只需要占用10M内存就能运行;但如果你不用free(),那么你的程序结束之前就会吃掉100G的内存。这其中当然包括绝大部分的虚拟内存,而由于虚拟内存的操作是要读写磁盘,因此极大地影响系统的性能。你的系统很可能因此而崩溃。
- 任何时候都为每一个malloc()写一个对应的free()是一个良好的编程习惯。这不但体现在处理大程序时的必要性上,更体现在程序的优良的风格和健壮性上。毕竟只有你自己的程序知道你为哪些操作分配了哪些内存以及什么时候不再需要这些内存。因此,这些内存当然最好由你自己的程序来回收。
6. 野指针
“野指针”不是NULL 指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if 语句很容易判断。但是“野指针”是很危险的,if 语句对它不起作用。
“野指针”的成因:
- 指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL 指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。
- 指针p被free 或者delete 之后,没有置为NULL,此时对指针p的操作,可能应该地址未分配而错误或者该地址被重新分配给其他变量时引起对其他变量数据的误更改,危害极大。
- 指针操作超越了变量的作用范围。
因此动态内存创建后,
free(p);
p = NULL;
两步必不可少。