一个典型C程序的的内存表达包括如下部分:
1. 文本段
2. 初始化数据段
3. 未初始化数据段
4. 栈区
5. 堆区
一个运行进程的典型的内存布局
1. 文本段
一个文本段,也被称作代码段或者简称为文本,是一个程序在对象文件或者在内存中的其中一部分,包含可执行的指令。
在内存区,文本段可能放在堆区或者栈区的下面,这主要是为了防止堆或栈的溢出从而覆盖它。
通常情况下,文本段是共享的,这样使得为了频繁执行的程序在内存中仅仅只需要一个拷贝,例如文本编辑器,C编译器,shell等等。另外,文本段通常是只读的,主要是为了防止程序被不小心地更改它的指令。
2. 初始化数据段
初始化数据段,通常也被称为数据段。数据段是一个程序的虚拟地址空间的一部分,它包含着被程序员初始化的全局变量和静态变量。
注意,数据区并不是只读的,因为它的值可以在运行时被改变。
这个段又可以进一步被分为初始化只读区和初始化读写区。
例如,在C中被定义为全局变量的char s[] = "hello world"和位于main(也就是全局的)以外的语句int debug=1,会被存储在初始化读写区。一个全局C语句,比如const char* string = "hello world",会将字符串值"hello world"存储在初始化只读区,而字符指针变量则会被存储在初始化读写区。
例如:static int i = 10,将会存储在数据区,同样,global int i = 10也会存储在数据区。
3. 未初始化数据段
未初始化数据段,经常被称为"bss"数据段,它以一个古老的汇编器运算符代表着"block started by symbol"来命名。在程序开始执行之前,这个段中的数据会被内核初始化为0。
未初始化段起始于起始于数据段的结尾,并且包含所有的全局变量和静态变量,这些变量会被初始化为0,而且没有在源代码中有显示地进行初始化操作。
例如一个变量声明为static int i;会被存放在BSS段。
再比如一个全局变量声明为int j;也会被存放在BSS段。
4. 栈
传统上栈区和堆区是紧邻的,但是增长方向相反;当栈的指针遇到了堆的指针,则表明空闲的内存被耗尽了。(在现代大地址空间和虚拟内存技术下,它们可能被放在任意的位置,但是它们的增长方向仍然是典型的相反。)
栈区包含程序栈,就是LIFO结构,典型地是位于内存的高地址部分。在标准的X86计算机架构下,它朝着地址为0的方向增长;在一些其它的架构下,它朝着相反的方向增长。一个“栈指针”寄存器跟踪栈的顶点,每次一个新的值压到栈中,该指针跟着进行调整。为了一个函数的调用而压进的值的集合称为“栈帧”;一个栈帧包含最小的返回地址。
自动变量和每一次函数被调用所保存的信息都存放在栈中。每一次函数被调用,返回的地址以及关于调用者环境的特定信息,比如一些机器寄存器,都会保存在栈中。新的调用的函数然后为其自动和临时变量在栈上分配空间。这就是在C语言中递归函数工作的原理。每一次递归函数调用它自己时 ,就会用到一个新的栈帧,所以一个变量的集合不会影响到该函数的其它实例的变量。
5. 堆
堆通常是动态内存申请所在的段。
堆区起始于BSS段的终点,并向着地址增大的方向增长。堆区通过malloc,realloc和free函数进行管理,可能会用到brk和sbrk系统调用来调整它的大小(注意brk/sbrk的使用和一个单一的“堆区”并不需要满足malloc/realloc/free的要求;它也可以通过使用mmap来实现逆转潜在的虚拟内存的非连续区域到进程的虚拟地址空间)。在一个进程中所有的共享库和动态加载模块共享堆区。
实例:
size(1)命令报告出文本段,数据段,和bss段的大小(以bytes为单位)(为了更深入的了解,请参考size(1)的man page)
1. 检查如下简单地C程序
#include <stdio.h>
int main(void)
{
return 0;
}
fengxi@ubuntu:~/C/memeory$ size memory-layout
text data bss dec hexfilename
1033 276 4 1313 521memory-layout
2. 让我们在程序中增加一个全局变量,然后再来检查bss的大小
#include <stdio.h>
int global; /* Uninitialized variable stored in bss*/
int main(void)
{
return 0;
}
fengxi@ubuntu:~/C/memeory$ size memory-layout
text data bss dec hexfilename
1033 276 8 1317 525memory-layout
3. 让我们增加一个静态变量,也是存储在bss中。
#include <stdio.h>
int global; /* Uninitialized variable stored in bss */
int main(void)
{
static int i; /* Uninitialized static variable stored in bss */
return 0;
}
fengxi@ubuntu:~/C/memeory$ size memory-layout
text data bss dec hexfilename
1033 276 12 1321 529 memory-layout
4. 让我们初始化静态变量,它将会存储在数据段中。
#include <stdio.h>
int global; /* Uninitialized variable stored in bss */
int main(void)
{
static int i = 100; /* Uninitialized static variable stored in bss */
return 0;
}
fengxi@ubuntu:~/C/memeory$ size memory-layout
text data bss dec hexfilename
1033 280 8 1321 529memory-layout
5. 让我们初始化全局变量,也将被存储在数据段中。
#include <stdio.h>
int global = 10; /* Uninitialized variable stored in bss */
int main(void)
{
static int i = 100; /* Uninitialized static variable stored in bss */
return 0;
}
fengxi@ubuntu:~/C/memeory$ size memory-layout
text data bss dec hexfilename
1033 284 4 1321 529memory-layout
此文翻译自 这里