1. RAM内存?
? ? ? ? ?RAM内存包括:代码段(text)、数据段(data)、bss段、堆栈段(head stack)?
?2. 编译器编译结果分析
? ? ? 编译结果有代码段(text)、数据段(data)、bss段。
- 代码段(.text)是可执行指令的集合;
- 数据段 (.data)?表示已经初始化不为0的存放在静态区的数据(全局 or静态)。
- .bss段 表示未初始化的或为0的存放在静态区的数据(全局 or静态)。
? ? ? ?从可执行程序的角度来说,如果一个数据未被初始化,就不需要为其分配空间,所以.data 和.bss 的区别就是 .bss 并不占用可执行文件的大小,仅仅记录需要用多少空间来存储这些未初始化的数据,而不分配实际空间。所以......
? ? ? ?代码段(text)、数据段(data)这两者相加共同构成可执行文件的大小,dec也就是文件大小(hex也是文件大小,只不过是16进制表示的)。
3. 堆栈
3.1 堆
- 堆保存函数内部动态分配(malloc 或 new)的内存,是另外一种用来保存程序信息的数据结构。
- 堆是先进先出(FIFO)数据结构。堆的地址空间是向上增加,即当堆上保存的数据越多,堆的地址越高。动态内存分配。
注意:堆内存需要程序员手动管理内存,通常适用于较大的内存分配,如频繁的分配较小的内存,容易导致内存碎片化。
3.2 栈
- 栈保存函数的局部变量(不包括 static 修饰的变量),参数以及返回值。是一种后进先出(LIFO)的数据结构。
- 在调用函数或过程后,系统会清除栈上保存的局部变量、函数调用信息及其他信息。
- 栈的另外一个重要特征是,它的地址空间 向下减少,即当栈上保存的数据越多,栈的地址越低。静态内存分配。
注意:由于栈的空间通常比较小,一般 linux 程序只有几 M,故局部变量,函数入参应该避免出现超大栈内存使用,比如超大结构体,数组等,避免出现 stack overflow。
4. 总结
段名 | 存储属性 | 内存分配 | 代码段 .text | 存放可执行程序的指令,存储态和运行态都有 | 静态 | 数据段 .data | 存放已初始化(非零初始化的全局变量和静态局部变量)的数据,存储态和运行态都有 | 静态 | bss段 .bss | 存放未初始化(未初始化或者0初始化的全局变量和静态局部变量)的数据,存储态和运行态都有 | 静态 | 堆 heap | 动态分配内存,需要通过malloc手动申请,free手动释放,适合大块内存。容易造成内存泄漏和内存碎片。运行态才有。 | 动态 | 栈 stack | 存放函数局部变量和参数以及返回值,函数返回后,由操作系统立即回收。栈空间不大,使用不当容易栈溢出。运行态才有 | 静态 |
5. 例子
#include <iostream>
using namespace std;
int a = 0;//初始化的全局变量:保存在数据段
char *p1;//未初始化的全局变量:保存在BSS段
int main()
{
int b;//未初始化的局部变量:保存在栈上
char s[] = "abc";//"abc"为字符串常量保存在常量区;数组保存在栈上,
并将常量区的"abc\0"复制到该数组中。这个数组可以随意修改而不会有任何隐患,
而"123"这个字符串依然会保留在静态区中。
char *p2;//p2保存在栈上
char *p3 = "123456";//p3保存在栈上,"123456\0"保存在data区的read-only部分
//注意:如果令p3[1] = 9; 则程序崩溃,指针可以访问但不允许改变常量区的内容
//声明了一个指针p3并指向"123456\0"在静态区中的地址,事实上,p3应该声明为
char const *,以免可以通过p3[i]='\n'这一类的语法去修改这个字符串的内容。如果这样
做了,在支持“常量区”的系统中可能会导致异常,在“合并相同字符串”的编译方法下会导致其它
地方的字符串常量古怪地发生变化。
static int c = 0;//初始化的静态局部变量:保存在数据区(数据段)
p1 = (char *)malloc(sizeof(char) * 10);//分配的10字节区域保存在堆上
p2 = (char *)malloc(sizeof(char) * 20);//分配的20字节区域保存在堆上
strcpy(p1, "123456");
//"123456\0"放在常量区,编译器可能会将它与p3所指向"123456"优化成一个地方
return 0;
}
|