__cdecl与__stdcall的核心区别在于栈清理责任:__cdecl由调用方清栈,__stdcall由被调函数清栈;两者压栈顺序均为从右向左,但名字修饰不同(__cdecl为\_Func,__stdcall为\_Func@N),影响链接与动态调用。
Windows平台下C++函数调用约定主要有 __cdecl、__stdcall、__fastcall、__thiscall 和 __vectorcall(较新版本VC支持)。其中最基础、最常被问及的是 __cdecl 与 __stdcall,它们的核心差异在于谁来清理栈空间和参数压栈顺序——而这直接关系到函数能否被正确调用、链接和导出。
__cdecl 和 __stdcall 都采用从右向左的压栈顺序。例如:
func(a, b, c);
实际入栈顺序是:c → b → a。这点没有区别,不必担心调用时参数错位。
函数调用后,栈指针(ESP)必须恢复到调用前位置,这个“清理栈”的动作由谁完成,决定了调用约定的本质差异:
add esp, N 指令(N为参数总字节数)。ret N(带立即数的 ret 指令),自动修正栈顶。这意味着:同一个函数如果声明为 __stdcall 却被按 __cdecl 方式调用(或反之),会导致栈失衡——轻则局部变量错乱,重则程序崩溃,且问题往往延迟暴露,极难调试。
为了支持函数重载和调用约定识别,编译器会对函数名进行修饰。两者典型规则如下(以32位x86为例):
_MyFunc;若带参数,不体现参数大小,仍为 _MyFunc。
__stdcall:函数名前加下划线,**后缀加 @ + 参数总字节数**,如三个 int 参数(12字节)→ _MyFunc@12。因此,在 .def 文件导出、GetProcAddress 动态获取、或混用 C/C++ 与汇编时,必须严格匹配调用约定,否则链接器报 “unresolved external” 或运行时报 “procedure not found”。
基本上就这些。理解它们不是为了手写汇编,而是读懂链接错误、排查崩溃、正确使用 Win32 API 或编写 DLL 导出函数时的关键底层支撑。