进程中内存空间的划分: 1. 代码区 – 存放代码/函数,只读区 2. 全局区 – 保存全局变量,读写区 3. BSS段 – 未初始化的全局变量,BSS段在main执行前会自动清0 4. 栈区 – 存局部变量,包括函数形参,栈区的内存是自动分配自动回收的 5. 堆区 – 程序员自己管理的区域,malloc/ free操作的都是堆区。 6. 只读常量区 – 存放字符串常量和const修饰的全局变量。 注:只读常量区和代码区合并在一起。 上面的这种划分在很多文档中都有看到,这里我不想再一一介绍。只想说一下其中的误解和陷阱,本文讲以一下几部分展开: 1)变量前缀和储存位置 2)为什么要有bss段,全局变量未初始化到底放哪? 3)linux察看内存分配的map文件 一,变量前缀 和 储存位置: 这部分帮助大家回顾const, static,auto,全局,局部等变量常量的储存位置。 首先看下面的一个小程序: const int i2 = 10; int * p2 = &i2; *p2 = 200; printf("%d\n",i2); 大家猜猜输出的结果。是不是会出现编译错误或者运行时错误? 答案是:输出200。运行时有警告:非常指针指向常变量。 再看下面的一段代码: 1 #include 2 int main() 3 { 4 char *a="asdaasasda"; 5 char *p = a; 6 *p = 'z'; 7 printf("%s\n", a); 8 return 0; 9 } 运行时会提示:段保护错误。 1 #include 2 char *f()
3 { 4 char *t = "happy!"; 5 return t; 6 } 7 int main() 8 { 9 char *a="asdaasasda"; 10 //char *p = a; 11 //*p = 'z'; 12 //printf("%s\n", a); 13 printf("%s\n",f()); 14 return 0; 15 } 执行结果:happy! 这样我们可以得出结论:多种声明方式容易搞混,而其实只有生存周期和存储位置的对应关系是不变的。长周期变量:必然存在全局区和常量区,而临时变量都存在栈区。反之亦然: 存在全局区的和常量区的都是长周期变量,栈区都是临时变量。 隐式声明:char *str = "XXXX";其实是static const的。或者可以理解在编译阶段字符串被抽取出来随文件一起保存。 二,可爱的BSS段:BSS段的全称是什么?很多人知道bss段是存储未初始化全局变量的,可是没有去想为什么系统要单独开辟一个区去存未初始化全局变量?bss区有什么特殊的地方,使得他适合去存储未初始化的全局变量?下面为大家解决这些问题: BSS(Block Started by Symbol),如果大家对汇编有所了解,肯定知道block的概念。我们在C代码中定义的变量都是按照block存的。数组名就是block首地址的symbol。一般都是这样的格式: AAA dw 0,1,2..... AAA后面没有冒号,在运行时如果用AAA做相对寻址,其单元大小都是dw。这就是汇编里对应数组的结构:block。 BSS段中有很多数组?对还是错?其实by symbol就是告诉我们有很多数组symbol,但是后面的具体数据并不在bss中。那么这就像一张列出数组symbols的表格了。那么为什么要搞这么一张表,不能直接存数据么?你可以想象如果数组很大,怎么把这么多数据写在二进制文件中。那将会使得文件很大。最关键的是,一会我们会看到,开始分配4G内存的时候,全局变量区的大小是定死了的!你放太大的数组会使得出现多个内存数据不连续片断。而局部变量不存在这个问题,栈是向上增长的,理论上没有容量上限。参看下图:
|