要明白C++中函数的堆栈调用,首先我们应该弄清楚这五个问题;
1、形参开辟内存吗?有谁开辟?
答:形参开辟内存,由调用方开辟。
2、形参的入栈顺序是什么?
答:从右到左。
3、返回值由谁带出?
答:寄存器。
4、被调用方结束后怎么回退到调用方上?
答:调用栈底指针的地址保存到被调用方栈底指针。
5、函数调用完一轮的下一条是怎么知道要继续进行下一行指令而不是从头开始执行?
答:调用时,将下一行指令的地址压栈,清栈过程中又方在了下一行指令寄存器中了。
下面我们举一个简单的例子来详细说明一下;
#include<stdio.h>
int sum(int lhs,int rhs)
{
int tmp=lhs+rhs;
return tmp;
}
int main()
{
int a=10;
int b=20;
int rt=0;
rt=sum(a,b);
printf("%d\n",rt);
return 0;
}
我们即将上面的代码转成反汇编来看看:
ebp :栈底指针寄存器(存放调用方栈底指针地址)
esp :栈顶指针地址(压入实参的指针)
pc :存放下一行指令的地址
开栈:
1、压入实参,形参初始化 自左向右
2、压入下一行指令地址
3、压入调用方栈底地址
4、调转到被调用方函数栈帧
5、被调用方开辟活动空间并初始化
清栈后数据还在,只不过告诉系统该区域可被再次分配
函数调用约定
1、_cdel c标准调用约定 (返回值 形参)
2、_stdcall windows标准调用约定
3、_fastcall 快速调用约定
4、_thiscall 成员方法的调用约定
调用规则:
1、函数符号的生成:不同调用约定下的函数无法进行调用
2、实参的入栈顺序:从右向左
3、形参内存的开辟和清理方式‘
_cdel 调用方开辟,调用方清理
_stdcall 调用方开辟,被调用方清理
_fastcall 没有开辟,使用最多两个寄存器带入实参,如果有形参开辟则调用方开辟,调用方清理