目录
1.C程序内存分布
一个典型的C程序的内存布局包含下面几个部分:
1) Text segment/Code segment (文本/代码段)
2) Initialized data segment (已初始化数据段)
3) Uninitialized data segment/BSS(Block Started by Symbol) (未初始化数据段)
4) Stack (栈)
5) Heap (堆)
一个正在运行的进程的典型内存分布:
1. Text Segment/Code Segment(文本/代码段)
文本段,也称之为代码段或者文本,它是一个程序内存的一部分,包含可执行指令。
做为一块内存区域,文本段一般处于堆/栈之下,以防止堆/栈将其内存覆盖了。
通常文本段是可以共享的,这样对于需要经常执行的程序,只需要一份copy存在于内存中。例如文本编辑器,C编译器,shell等。
另外,文本段一般是只读的,防止外边程序修改其指令。
2. Initialized Data Segment(已初始化数据段)
已初始化数据段,通常简称为数据段。包括一个程序中已经被初始化过的全局变量以及静态变量。
注意:数据段不是只读的,因为变量值在运行过程中可能会被更改。
数据段可以进一步分为已初始化的只读区,以及已初始化的读写区。
示例1:C程序中的全局字符串 char s[] = "hello world",以及main函数之外的C声明debug=1,都将存储在读写区。
示例2:const char *string = "hello world", 一个全局的C声明,会使得字符串"hello world"存储在只读区,指针string存储在读写区。
示例3: static int i = 10; 全局的int i = 10; 已初始化的静态数据和全局数据,也会存储在此区域。
3. Uninitialized Data Segment/BSS (未初始化数据段)
未初始化数据段,常称为"BSS"段。以一个古老的汇编语言操作符命名,表示"block started by symbol". 这个段里边的数据会在程序开始执行前被内核初始化为算术0。
BSS段位于(初始化)数据段之后,包含所有的全局变量,以及初始化为0或者没有被显示初始化的静态变量。
例如:static int i; 全局的int j; 像这些未初始化的静态或全局数据,会存储在此区域。
4. Stack
栈区通常是与堆区相邻的,并且是相反方向增长;当栈指针与堆指针重合时,表示剩余内存已经全部耗尽(在现代的大内存空间和虚拟内存技术的背景下,栈内存可能任意分配,但是总体上任然是反方向增长)。
栈区包含程序栈,是一个LIFO(后进先出)结构,通常位于内存的高地址部分。在通常的x86架构体系中,往地址0方向增长;在其他的一些架构中,往反方向增长。
一个“栈指针"指向栈顶;每次当push一个值到栈中时,栈指针会调整。压入一个函数调用的所有的值称为一个”栈帧";一个"栈帧"至少包含一个返回地址。
栈用于存储自动变量,函数每一次调用的值都存储在其中。每次函数被调用,返回地址以及调用者的环境信息(如某些寄存器)会被存储在栈中。新调用函数会在栈中为其自动变量以及临时变量分配内存。
这就是C中递归函数能工作的原因。每次一个递归函数调用自己,一个新的栈帧会被使用,这样本地调用中的变量值不会干扰下一次的调用。
5. Heap
堆通常用于动态内存管理。
堆开始于BSS段末尾,并且往高地址增长。堆内存由malloc, realloc, 和free管理,可能会使用系统调用brk和sbrk来调整大小(注意,brk/sbrk和一个单独的堆区并不一定等价于malloc/realloc/free, 它们也可能使用mmap来预留虚拟内存的潜在非连续区域,加入到进程的虚拟地址空间中)。
在一个进程中,堆区在所有的共享库之间共享,并动态地加载模块。
2.示例详解
以下所有例子都运行在环境: CentOS7.2 + gcc4.8.5
2.1查看内存分布
Linux上的size(1)命令可以查看text, data以及BSS段的大小(字节为单位)。
#include <stdio.h>
int main(void)
{
return 0;
}
bash-4.1$ gcc memoryLayout.c -o memoryLayout
bash-4.1$ size memoryLayout
text data bss dec hex filename
1129 540 4 1673 689 memoryLayout
2.2未初始化全局变量
#include <stdio.h>
int global; //未初始化数据段BSS
int main(void)
{
printf("&global=%p", &global);
return 0;
}
bash-4.1$ gcc memoryLayout.c -o memoryLayout
bash-4.1$ size memoryLayout
text data bss dec hex filename
1229 548 12 1789 6fd memoryLayout
bash-4.1$ ./memoryLayout
&global=0x601038
由上可知,未初始化全局变量,确实处于BSS段中。
2.3未初始化静态变量
#include <stdio.h>
int global; //未初始化数据段BSS
int main(void)
{
static int localStatic; //未初始化静态变量BSS
printf("&global=%p\n", &global);
printf("&localStatic=%p\n", &localStatic);
return 0;
}
bash-4.1$ gcc memoryLayout.c -o memoryLayout
bash-4.1$ size memoryLayout
text data bss dec hex filename
1279 548 12 1839 72f memoryLayout
bash-4.1$ ./memoryLayout
&global=0x60103c
&localStatic=0x601038
2.4已初始化静态变量
#include <stdio.h>
int global; //未初始化数据段BSS
int main(void)
{
static int localStatic = 100; //已初始化静态变量,存储在数据段
printf("&global=%p\n", &global);
printf("&localStatic=%p\n", &localStatic);
return 0;
}
bash-4.1$ gcc memoryLayout.c -o memoryLayout
bash-4.1$ size memoryLayout
text data bss dec hex filename
1279 552 8 1839 72f memoryLayout
bash-4.1$ ./memoryLayout
&global=0x60103c
&localStatic=0x601034
2.5初始化全局变量
#include <stdio.h>
int global = 99; //初始化的全局变量,存储在数据段
int main(void)
{
static int localStatic = 100; //初始化的静态变量,存储在数据段
printf("&global=%p\n", &global);
printf("&localStatic=%p\n", &localStatic);
return 0;
}
bash-4.1$ gcc memoryLayout.c -o memoryLayout
bash-4.1$ size memoryLayout
text data bss dec hex filename
1279 556 4 1839 72f memoryLayout
bash-4.1$ ./memoryLayout
&global=0x601034
&localStatic=0x601038