栈帧(函数调用)
我们都知道在写一个函数时会使用形参,形参实例化时会形成一份拷贝,调用这个函数时会把实参传进去,调用完之后那些临时拷贝又被释放,那么计算机在调用函数时是如何进行形参的保存和释放的呢?又如何返回形参?我们可以通过栈帧来理解函数的调用原理。
首先,栈是从高地址向低地址延伸的。每个函数的每次调用都有它自己独立的一个栈帧,这个栈帧中有它所需要的各种信息。每个函数都有自己的一份esp和ebp,而CPU只有一份esp和ebp。因为ebp和esp永远保存最新当前函数的值,所以每个函数都有自己的esp和ebp指向栈顶和栈底。
esp:栈顶寄存器,指向栈的低地址。
ebp:栈底寄存器,指向高地址。
pc指针:指向当前运行程序指令的下一条指令。
call指令: ①保存当前指令下一条指令的地址(方便返回)
②通过jmp跳到指定函数入口地址处(fun函数)
黑色线指的是main函数.
红色线指的是被调用者fun函数.
蓝色线指的是fun函数返回时,恢复到调用之前的状态.
CPU在重复的执行三个操作:取指令、分析指令、执行指令,取指令通过pc指针,分析指令通过CPU里的各种指令集。
接下来我们看一段代码:
#include<windows.h>
#include<iostream>
using namespace std;
int fun(int x,int y)
{
intc = 0xcccccccc;
returnc;
}
int main()
{
inta = 0xaaaaaaaa;
intb = 0xbbbbbbbb;
intfir = fun(a, b);
cout<< "lucky!" << endl;
system("pause");
}
我们可以按F10进入调试,选择反汇编,请看截图
由图可看出参数实例化的时候是从右往左的
调用fun函数栈帧形成的过程:
push ebp 首先将pc指针下移然后把ebp寄存器的内容(main函数的栈底地址)
存到fun函数的当前栈顶(当前esp指向的地址);
mov ebp esp 把esp的内容赋给ebp, fun函数的栈底;
sub esp,0E4h 给esp减去一个随机值,指向一个新的fun函数栈顶;
fun函数返回时的过程:
mov esp ebp 把esp指向ebp的内容,形成新的栈顶
pop ebp 将栈顶的内容(main:ebp)弹出来放在ebp(地址空间图上的第一个把esp指针上移
ret 把main:retaddr返回给pc ,esp上移;使pc指向main函数的下一条指令
fun函数返回后a和b就会被释放esp会回到之间的位置
这就是fun函数调用的原理(不是很详细)。
欢迎大家指出来当中的错误。