程序员应了解的那些事(17)C语言过程活动记录(Active Record)/ ESP、EBP寄存器
【AR-1】
活动记录,就是在程序执行的过程中函数调用时栈上的内容变化。一个函数被调用,反映在栈上的与之相关的内容被称为一帧,其中包含了参数、返回地址、老ebp值、局部变量以及esp和ebp。
C语言默认的调用规约为cdecl,参数从右往左依次入栈,之后是函数的返回地址入栈,接着是老ebp入栈。
ESP是栈顶指针,EBP就基指针寄存器,用它可以存取栈中的数据,向上(栈底方向)可以获取返回地址、参数值, 向下(栈顶方向)可以获取函数局部变量值。一般而言,SS:[ebp+4]处为返回地址;SS:[ebp+8]处为第一个参数值,SS:[ebp+0Ch]为第二个参数值;SS:[ebp-4]为第一个局部变量,SS:[ebp]处为上一层ebp值。
注1:__cdecl 是C Declaration的缩写(declaration,声明),表示C语言默认的函数调用方法:所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈。
注2:esp是栈指针,是cpu机制决定的,push、pop指令会自动调整esp的值;
ebp只是存取某时刻的esp,这个时刻就是进入一个函数内后,cpu会将esp的值赋给ebp,此时就可以通过ebp对栈进行操作,比如获取函数参数,局部变量等,实际上使用esp也可以;
既然使用esp也可以,那么为什么要设定ebp呢?答案是为了方便程序员。因为esp在函数运行时会不断的变化,所以保存一个一进入某个函数的esp到ebp中会方便程序员访问参数和局部变量,而且还方便调试器分析函数调用过程中的堆栈情况。前面说了,这个ebp不是必须要有的,你非要使用esp来访问函数参数和局部变量也是可行的,只不过这样会麻烦一些。
【AR-2】(参考C专家编程 6.5节)
C 语言自动提供的服务之一就是跟踪调用链——哪些函数调用了哪些函数,当下一个return语句执行后,控制将返回何处等。解决这个问题的经典机制是堆栈中的活动记录。
当每个函数被调用时,都会产生一个过程记录(或者类似的结构)。过程活动记录是一种数据结构,用于支持过程调用,并记录调用结束以后返回调用点所需要的全部信息。
每当有一个函数调用时,就会在堆栈(Stack)上准备一个被称为AR(activation recored)的结构,抛开具体编译器实现细节的不同,这个AR基本结构如下所示。
每当遇到一次函数调用的语句,C编译器都会产生出汇编代码来在堆栈上分配这个AR。分析下面代码:
void a(int i)
{
if(i > 0){
a(--i);
}
else
{
printf("i = %d \n", i);
}
}
int main(int argc, char** argv)
{
a(1);
}
在这一段代码中调用的顺序如下:
上一篇: struts配置导出表格