运行时结构有好几种:堆栈、活动记录(activation record)、数据、堆等
堆栈段
堆栈段包含了一种单一的数据结构---堆栈。堆栈是一个经典的计算机科学对象。它是一块动态内存区域,实现了一种“先进后出”的结构,有点类似于自助餐厅里叠在一起的盘子。堆栈的经典定义是它可以放置任意数量的盘子,但唯一有效的操作就是从顶部放或取一个盘子。也就是说,值既可以压到堆栈中,也可以通过出栈取得值。入栈操作使堆栈变长,出栈操作从堆栈中取出一个值。
编译器设计者采用了一种稍显灵活的方法。我们可以从顶部增加或拿掉盘子,也可以修改位于堆栈中部的盘子的值。函数可以通过参数或全局指针访问它所调用的函数的局部变量。运行时系统维护一个指针(常位于寄存器中),通常称为sp,用于提示堆栈当前的顶部位置。
堆栈段有3个主要的用途,其中两个跟函数有关,另一个跟表达式有关。
*堆栈为函数内部声明声明的局部变量提供存储空间。按照C语言的术语,这些变量被称为自动变量。
*进行函数调用时,堆栈存储与此有关的一些维护性信息,这些信息被称为堆栈结构(stack frame),另外一个更常用的名字是过程活动记录(precedure activation record)。现在只要知道它包括函数调用地址(即所调用的函数结束后跳回的地方),任何不适合装入寄存器的参数以及一些寄存器值的保存即可。
*堆栈也可以被用作暂时存储区。有时候程序需要一些临时存储。通过alloca()函数分配的内存就位于堆栈中。如果想让内存在函数调用结束之后仍然有效,就不要使用alloca()来分配(它将被下一个函数调用所覆盖)。
除了递归调用之外,堆栈并非必需的。因为在编译时刻可以知道局部变量、参数和返回地址所需空间的固定大小,并可以将它们分配于BSS段。允许递归调用意味着必须找到一种方法,在同一时刻允许局部变量的多个实例存在,但只有最近被创建的那个才能被访问,这很像堆栈的经典定义。
/*堆栈的大致位置*/
#include <stdio.h>
int glob_var = 1;
int glob_unit_var;
static int local_var = 2;
static int local_unit_var;
int main() {
int i;
int j[5];
printf("glob_var = %p\nglob_unit_var = %p\nlocal_var = %p\nlocal_unit_var = %p\n\n", &glob_var, &glob_unit_var, &local_var, &local_unit_var);
printf("The stack top is near %p\n\n", &i);
for (int index = 0; index < 5; ++index) {
printf("&j[%d] = %p\n", index, &j[index]);
}
return 0;
}
输出:
堆栈向上增长的,也就是朝着低地址方向生长。相信你肯定还能看出其他的结论。
要发现数据段和文本段,以及位于数据段内的堆,方法是声明位于这些段的变量,并打印它们的地址。通过调用函数和声明一些大型局部数组使堆栈增长。
在不同的计算机架构中和不同的操作系统中,堆栈的位置可能各不相同。事实上在绝大多数处理器中,堆栈是向下增长的,也就是朝着低地址方向生长。