- __cdecl, __stdcall, __fastcall
- __cdecl, __stdcall 的区别
- 函数栈帧末端处理
- 函数出栈后处理
- __stdcall 与 __fastcall 区别
- 调用前
- 调用中
- 总结
Calling conversion,中文叫:调用约定
__cdecl, __stdcall, __fastcallVS C++中默认的函数调用约定是__cdecl(我的VS是2019版本的,可能其他版本的不是__cdecl的),如下图:
如下图所示,MSVC编译器提供了四种调用约定:
下面是在 Debug 模式下的反汇编代码。(因为Release会优化而删减这个简单例子的代码)
__cdecl, __stdcall 的区别 函数栈帧末端处理 可以看到__stdcall有清理参数压栈的空间,__cdecl的没有处理清理参数压栈空间,__cdecl是在caller调用函数中清理的
__cdecl在caller中清理了之前的三个push的栈空间,__stdcall就没有处理了(__stdcall在callee中清理了)
从调用前,可以看到__fastcall的参数入栈方式与__stdcall的不一样
- __fastcall 先将 int c参数压栈了
- __stdcall 将c, b, a都压栈了
下面再看看调用中的栈帧处理
调用中可以看出来,__fastcall 调用中的才将b, c直接设置栈帧内存数据,而不像__stdcall那样,在调用前就将参数push到栈帧内存数据中了。
但从这点看起来,__fastcall也没有多fast呢。。但还是加快了一点点的:后两个b, c参数都不用每个push,而是在callee函数里直接设置栈内存值。因为一个push回有stack[sp--] = value
,相当于一个mov dword ptr[esp], value
,然后在sub esp, 1
,而__fastcall块在如果参数很多,那么就能节省掉多次的sub esp, 1
。
调用后的处理只是接收 eax 结果。这点__fastcall,__stdcall都一样的。(__cdecl会先清理上个栈帧的内容)
总结__cdecl,__stdcall,__fastcall 三种方式都是从右向左将参数压栈的。 __cdecl 是在 caller 清理栈的,所以可以处理变长的参数,因为都同同一栈帧处理。 __stdcall、__fastcall 都是在 callee 清理栈的,所以不可以处理变长参数。
约定参数压栈顺序栈清理__cdecl右 -> 左caller__stdcall右 -> 左callee__fastcall右 -> 左callee