C++可执行程序内存结构
C++ Review
1. 典型C/C++程序内存表示
一个典型的C/C++内存表示分为如下几个部分:
- 代码段
- 初始化数据段
- 未初始化数据段
- 堆
- 栈
具体分布如图所示:
代码段
代码段或文本段是简单的文本,它是对象文件或内存程序中的一段,包含了可执行的指令,作为内存区域,代码段被放置在堆或栈的下面,以防堆栈溢出覆盖它。通常代码段是可分享的,因此当程序文件被频繁执行时只有一份代码段会被拷贝进内存,如文本编辑器、C编译器,shell程序等等。另外代码段是只读的,为了防止一个程序不小型修改了其中的指令。
文本段
字面常量
已初始化数据段(数据段)
已初始化数据段又称数据段,它是程序虚拟地址空间的一部分,其中包含已经由程序员初始化的全局变量以及静态变量。需要注意的是,数据段不是只读的,因为变量的值可以在运行时更改,例如:
char s[] = "hello world"; //全局变量 |
上述两个已经初始化的全局变量都会被放在数据段,而如:
const char* string = “hello world”; |
这样的表达式,其中字面量“hello world”会被存放到已初始化的只读数据区域,而字符指针变量会被存放到已初始化的可读可写数据区域。
未初始化的数据段
未初始化的数据段又称为bss段,是“block started by symbol.”的缩写。在程序开始执行未初始化的数据之前,内核会将此段中的数据初始化为算术0。数据从数据段末尾开始,并包含所有初始化为零或在源代码中没有显式初始化的全局变量和静态变量。例如:static int i;
会被包含在bss段,int j;
会被包含在bss段。
栈(或堆栈)
栈区域传统上与堆区域相邻,并以相反的方向生长;当栈指针与堆指针相遇时,可用内存耗尽。栈区域包含程序堆栈,即LIFO结构,通常位于内存的较高部分。在标准的PC x86计算机体系结构上,它向零地址增长;在其他一些架构上,它的增长方向相反。“堆栈指针”寄存器跟踪堆栈的顶部;每次将值“推”到堆栈上时,都会对其进行调整。一个函数调用推送的一组值被称为“堆栈帧”;堆栈帧至少包含一个返回地址。
每次调用函数时,返回位置的地址和有关调用方环境的某些信息(如某些机器寄存器)都保存在堆栈上。然后,新调用的函数在堆栈上为其自动变量分配空间。这就是C语言中递归函数的工作原理。每次递归函数调用自己时,都会使用一个新的堆栈框架,因此一组变量不会干扰函数另一个实例的变量。
堆
堆区是进行动态内存分配的场所,堆区开始于bss段的末尾,朝着高地址的方向增长。堆区通过malloc, realloc以及free函数进行管理。堆区由进程中的所有共享库和动态加载的模块共享。