在函数调用时,计算机用栈来维持函数调用的上下文信息,我们称之为栈帧。
Linux和windows下栈的大小都是预先确定的,大小为1M,这个大小是可以更改。
调用函数前,首先把主调函数下一指令的地址入栈,然后函数的参数入栈。
调用函数前有两个比较重要的问题要解决:
1、参数入栈的顺序?
2、谁来清理栈内存?函数调用约定则解决了这两个问题:
_cdecl C调用方式,是C语言缺省的调用方式
1、参数从右向左依次压入堆栈
2 、主调方开辟形参内存,被调用方自己释放栈内存
3、编译时函数名自动加前导下划线。
_stdcall windows标准的调用约定
1、 参数从右向左依次压入堆栈
2、调用方开辟形参内存,被调用方自己释放内存
3、编译时函数名自动加前导下划线,后面紧跟着一个@,其后紧跟着参数的尺寸
_fastcall 快速调用约定
1、参数从右向左依次压入堆栈
2、调用方开辟形参内存,但是把最左边的(也就是最后8字节的实参通过寄存器带到被调用函数里)
被调用方自己释放内存
3、编译时函数命名规则同stdcall
_thiscall thiscall调用约定意味着:
1、参数从右向左依次压入堆栈
如果参数个数确定,this指针通过ecx传递给被调用者;
如果参数个数不确定,this指针在所有参数压入栈后被压入栈
2、参数个数不定的:由调用者清理堆栈,否则由函数自己清理堆栈。
3、thiscall 调用方式是唯一一种不能显示指定的修饰符因为它是c++类成员函数缺省的调用方式。
由于成员函数调用还有一个this指针,因此必须用这种特殊的调用方式。
对于参数个数固定的情况,它类似于stdcall,不定时则类似于cdecl。
_naked call参数入栈顺序是从左向右,比较复杂,已经很少用
如果定义的约定和使用的约定不一致,则将导致堆栈被破坏,导致严重问题,下面是两种常见的问题:函数原型声明和函数体定义不一致
DLL导入函数时声明了不同的函数约定
导致的结果是在调用函数的时候将栈帧破坏了。