C中的内存布局

时间:2022-09-09 19:57:26

原文

典型的c程序内存由以下几个部分构成:

1. 代码段(Text segment )
2. 数据段(Initialized data segment)
3. BSS段(Uninitialized data segment)
4. 栈(stack)

5. 堆(heap)

C中的内存布局

1.代码段

通常被用来存储程序的可执行代码,这部分区域大小在程序运行前就已经确定。

操作系统在装载一个程序时,代码段通常位于最底部即最低地址的部分,堆和栈在内存空间高处,从而避免堆栈溢出时覆盖代码段。

通常代码段是可共享的,所以对频繁运行的程序,在内存中只需要有一份代码段的拷贝,例如文本编辑器,C compiler,shells等。代码段经常是只读的,以此来避免程序无意间修改了自己的指令。

2. 数据段

已初始化的数据段,通常也被简单叫做数据段。数据段是包含了程序已初始化的全局变量和静态变量的一段内存空间。数据段不是只读的,因此变量的值可以在运行时被改变。

数据段又可以细分为只读区和读写区。

全局空间上的char s[] = “hello world”和 int debug = 1 会存储在读写区域。而像char* string = “hello world"中的字符串字面量“hello world"则被存储在只读空间中,而字符串指针string位于读写空间。

3. BSS段

Uninitialized data segment通常被称为BSS段(Block Started by Symbol的简称,这是一条古老的汇编指令,用于定义符号并且为该符号预留给定数量的未初始化空间)。在程序执行之前,BSS段中的数据被内核代码初始化为0。

BSS段位于数据段之后,包含了所有的未在代码中显式初始化的全局和静态变量。

4.栈

通常栈毗邻堆并且向着相反方向增长。当栈指针遇上堆时,空间耗尽。(在当今大地址空间和虚拟内存技术下,栈可以在任何地方,但是依然和堆向相反方向增长。)

栈是一个LIFO(last in first out先进后出)的结构。在标准的x86构架下它向0地址增长,而在另一些构架下它向着相反方向增长。一个“栈指针”寄存器被用于定位栈顶,每次有值入栈时调整栈指针的位置。一次函数调用过程中入栈的数据的集合被称为一个“栈帧(stack frame)”,一个栈帧最起码要包含返回地址。

栈是局部变量(automatic variables)存储的地方,每次函数调用的信息都存放在栈上。每一次的函数调用时,函数返回之后的地址以及调用者的信息都被存储在栈上。最近调用的函数会在栈上为它的自动变量和临时变量分配空间。这也是C函数递归的工作方式,每一次递归都会有新的栈帧被使用,所以一个函数实例中的数据集合不会受到另一个函数实例的影响。

5.堆

堆通常用于动态分配内存空间。

堆空间从BSS段结束的地方开始,向高地址增长。在C中,堆空间用 malloc, realloc, 和free来管理,可以通过 brk、sbrk等系统调用来跳转堆的大小。同一个进程中所有的库和动态加载模块都共享一块堆空间。