典型的基于PC的函数调用栈格式如下:
push ebp
mov ebp, esp
sub esp, 4*3
...
mov esp, ebp
pop ebp
ret
解释:
首先对于一个进程,栈平衡是必须的。在进入某进程的某个子函数的调用前,栈顶指针esp和栈基址(栈底指针)ebp有个差值。ebp在函数调用期外作为栈基址(这个栈基址值在进程生命期不变),但在调用期内,又是作为栈寻址指针来用的,所以在第一行需要把ebp保存起来,在最后一行又将ebp弹出,这两步保证整个调用结束前后的ebp不变。第二行将esp的栈顶值赋给ebp,让ebp作为栈寻址指针,而esp始终作为栈顶指针,所以第三行需要为实参预留顶空间。第四行为函数调用结束后,将栈指针ebp赋给esp作为栈顶指针,而栈基址不变。
所以对于一个N层的递归调用,在递推阶段,esp的值不断减小,即栈不断加长,主要是为了保存每层递归的返回地址,所以在每层调用前,ebp-esp的值都不一样,它反映了整个递归调用的调用栈大小;在回归阶段,esp又是不断增加的,即向ebp靠近。而在整个过程,ebp的值是不变的,当然在调用栈内ebp会作为栈寻址指针使用,此时的值肯定会临时改变,但调用结束后会恢复原值。