函数在进行调用时会产生开栈和清栈的操作,那么就来介绍一下函数调用堆栈的过程吧
首先,利用一个小例子来研究这个过程:
#include<iostream>
int Fun(int a,int b)
{
int tmp = 0;
tmp = a + b;
return tmp;
}
int main()
{
int a = 10;
int b = 20;
int res = 0;
res = Fun(a, b);
std::cout << res << std::endl;
return 0;
}
我们通过调试的反汇编功能可以看到主函数以及Fun函数执行时的反汇编代码:
main函数:
Fun函数:
在看这个之前,我们得先了解一下基本符号的意义
1)eax ebx ecx edx:均为寄存器,用来存储数据;
2)ebp esp(ebp为栈底指针,esp为栈顶指针);
3)mov eax,10 =>eax = 10;给eax寄存器赋值为10;
4)lea eax,[a] 将a的地址赋值给eax寄存器;
5)push 入栈
6)pop 出栈
7)sub add 加减运算
8)mov eax,dword ptr[a] 将a的值移动到eax中
接下来详细的解释一下整个过程:
总结一下:
- 首先开辟调用方栈帧,将栈帧初始化;
- 压入实参,然后自右向左初始化形参;
- 将call指令的下一行指令的地址压入栈;
- 将调用方栈底地址压入栈中;
- 跳转到被调用方的栈帧中,并开辟内存;
- 函数返回值返回
1)返回值 < 4字节 利用eax寄存器带回;
2)4字节 < 返回值 < 8字节 利用eax,edx寄存器带回;
3)返回值 > 8字节 利用临时量带回; - 利用栈中存储的调用方栈底的指针回退到调用方栈中;
- 调用约定
1)_cdecl C标准调用约定
2)_stdcall windows标准调用约定
3)_fastcall 快速调用约定
4)_thiscall 类成员方法的调用约定