下图为操作系统内存地址空间图:
1.
栈(stack):存储
局部变量,
函数参数,
返回地址等。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。
栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
2.堆(heap):存放动态分配(malloc)的内存。
一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS(Operating System)回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
//在C++中需区分堆和自由存储区(自由存储是C++中通过new与delete动态分配和释放对象的抽象概念,而堆(heap)是C语言和操作系统的术语,是操作系统维护的一块动态分配内存。new所申请的内存区域在C++中称为自由存储区。藉由堆实现的自由存储,可以说new所申请的内存区域在堆上。堆与自由存储区还是有区别的,他们并非等价)
3.
全局(静态static)区(.bss段/.data段):存放
全局变量和
静态变量。包含.bss段(存放未初始化或初值为0的全局变量和静态局部变量)和.data段(存放已初始化或初值非0的全局变量和静态局部变量),注意:在C++中不会去区分全局变量是否初始化,他们共同占用同一块内存区域。
4.文字常量区(.rodata段):常量字符串就放在这里的不允许修改,程序结束后由系统释放。
5.
代码存储区(.text段):存放
函数二进制代码。
示例代码如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include #include int char int { } |
--------------------------------------------------------------------------------------------
堆和栈的生长方向不同:
堆的生长方向是向着内存地址增加的方向,
栈的生长方向是向着内存地址减小的方向。当栈和堆生长到开始相互覆盖时,则称为
“栈堆冲突”,系统肯定垮台。
判断栈是由高地址向低地址生长
代码如下:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include< static static { } int { } |
输出结果为:
fir
: 0028FEFF0028FEFF
sec : 0028FEFF 0028FECF
stack grew downward
---------------------------------------------------------------------------------------------
find_stack_direction函数使用函数递归的方法
第一次进入,由于addr为NULL,所以将字符变量dummy的地址赋值给静态变量addr
第二次进入,由于静态变量addr已赋了值,所以进入 "Second entry."
接着,将第二次进入的dummy地址和第一次进入的dummy地址相比较
如果值非负,则堆栈向高地址增长;否则,堆栈向低地址增长
------------------------------------------------------------------------------------------------
对内存的基本了解后,也引发了我的一些思考:
1
2 3 4 5 6 7 |
#include int { } |
这段代码在定义指针*x的时候
并没有明确它的指向(未初始化),该指针可能指向任意一块内存,之后第五行代码则是对该处内存区域的数据进行修改,这样的操作很可能访问到非法内存导致段错误,当然也可能因为没访问到非法内存而没有产生段错误,但是一个健壮的程序不允许存在这样的隐患。所以说对数据的初始化是不可缺少的习惯。
可能出现内存泄露的几种情况:
情况一:同时当我们在使用malloc多次申请内存的时候(申请堆区),一定要进行相应的
free来释放指针所指向的地址空间。如果多次malloc而只进行一次free的话,也会造成内存泄露。
情况二:malloc申请地址空间后,还未用free释放该空间时就将指向该空间的指针置为NULL。
接下来说说free这个函数,来看下面这段程序:
1
2 3 4 5 6 7 8 9 10 11 12 13 |
#include #include int { } |
运行结果:
0028FF1C 3544168
0028FF1C 10
0028FF1C 3544168
我们知道
free函数的用法是释放指针所指的内存空间(切记不是释放指针),然后根据运行结果我们可以推断出实际上虽然内存空间已被释放,但是该指针若不置为NULL(
切记free后将指针置为NULL),则它所指向的地址空间还是刚刚已经释放的地址空间(上述程序显示指针指向的那块内存空间地址未发生改变)。
我们可以看到原本该地址空间的值为3544168,当我们修改该地址空间的值让其变为10后再释放该空间,接下来如果我们访问这块空间,会发现这块地址空间居然还是3544168,而不是10。至于为什么,我们不去追究,不同的操作系统有不同的解决方式。
其实我想说的只有两点:
(1).你可以不遵守规则,但不等于没有规则。
(2).不遵守规则而产生的结果是不可预测的。
https://segmentfault.com/q/1010000000253786(这里是对free释放内存后,对于内存空间疑问的讨论)
总结:
我们应该从这些点滴看起,当我们遇到一个很大的项目时,如果我们能在这些地方做到细微的注意,那么我们将会写出健壮的代码。
----------------------------------------------------------------------------------------------
参考资料:
http://www.cnblogs.com/QG-whz/p/5060894.html(C++自由存储区不等价于堆的解释)
http://crazyof.me/blog/archives/995.html(堆栈及内存动态分配的解释,非常详细,推荐阅读!!)