函数调用栈是理解函数调用的关键一环,也是理解递归思想的重要基础,本博文综合源码,汇编语言,寄存器,内存进行详细阐述
目录
一 源码
int add(int a, int b){
return a + b;
}
int main()
{
int a = 0;
int c = 3;
int m = add(0, 3);
return 0;
}
对上源码在windows vc6.0 下进行编译
二 汇编语言预计调用栈分析
2.1 编译器进入调试状态,并查看源码和对应的汇编代码
2.2 分析main 函数的函数栈
- 将调用main()函数的EBP 入栈
- mov ebp, esp 将 esp 赋值给ebp, 使得 ebp = esp 都指向 main()的EBP
- sub esp, 4ch, 即将esp 往下拉4ch
- int a =0, int c= 3, 即push 3, push 0, esp 继续往下移动
- 执行 add(0, 3) ,即 ,
- 函数将跳入 00401005, 再跳入 00401020(即 add 函数)
- 在跳入00401020 的时候 将 0040107F 赋值给EIP
2.3. add 函数的调用和返回
add 函数的调用栈变化过程和main() 函数基本相同,这里不再重复。重点分析add()函数的返回
即 mov esp, ebp
pop ebp
ret
这三句
当执行mov esp, ebp 时,将 ebp 的值赋给esp , 而此时EBP 正指向 main()函数的 EBP
当执行pop ebp 时, 值将ebp 执行的内存的值赋值给 ebp , 此时 ebp 回到 调用 add()之前指向的位置
执行完ret 后,EIP 重新指向 0040107F, 程序继续往下走
三 总结
通过以上分析,关于函数调用栈,可得出以下重要结论
1. 调用函数之前,首先将 参数入栈
2. 当刚进入被调用的函数,将返回地址入栈(返回地址即是调用函数完成后继续往下走的地址), 将调用函数的EBP 入栈
3. 函数返回时,调用函数的EBP 被回复到调用之前,ESP 也恢复到调用之前,EIP 指向返回地址(即程序继续往下走)