首先要了解
1.函数调用就要开辟栈空间,是用于本次函数调用中的临时变量的保存、现场保护。
这块栈空间我们称之为函数栈帧。
2.栈空间是向低地址生长。堆区与栈区相对生长的。
利用一个例子来解释函数调用、栈帧的创建和销毁。
#include<stdio.h>
int fun(int a, int b)
{
int z = a+b;
return z;
}
int main()
{
int a = 2;
int b = 3;
int c = fun(2,3);
pritnf("%d", c);
return 0;
}
我们发现其实main函数也是被调用的,所以也会形成栈帧。
栈帧有栈底(ebp)、栈顶(esp)。栈顶是入栈和出栈的唯一地方。
main函数为临时变量a、b开辟空间,如图
下一步,调用fun函数。调用fun函数需要传参。所以,先为参数开辟空间
此时看汇编代码:
push:参数压榨,先压b(函数传参从右往左)
call:1.将当条指令的下一条指令的地址保存至栈中。
2.将函数入口地址放至eip(寄存器,存放的是当前指令的下一条指令的地址)
此时栈空间如图:
1. 储存下一条指令的地址是为了,再调完fun函数时候可以返回至main函数中,继续执行之后
的代码。
2. 储存main(esp)同样是为了在调完fun函数之后,esp、ebp可以指向main函数的栈空间。
再所用东西都准备好之后,为fun函数开辟栈空间。
(此时寄存器esp\ebp存放的是(fun esp\ebp))
创建变量z,如图:
将a+b的值存z中,return z(返回值先存放入eax中)
函数fun调用结束,出栈释放栈帧。(这里要注意的是,释放空间并不是使栈空间清零)
此时汇编指令
pop:出栈。
ret:出栈一次,并将出栈的内容当作地址,将程序跳至该地址处。
(寄存器esp\ebp存放的是(main esp \ebp))
创建c变量如图:
继续执行之后的程序,至程序结束。