函数调用约定是C++中用于规范函数调用时参数传递顺序、栈空间维护责任、返回值处理方式的一组规则,不同的调用约定会直接影响编译生成的汇编代码逻辑,也会影响不同模块之间的函数兼容性。

常见的C++函数调用约定
1. __cdecl 调用约定
__cdecl是C和C++程序的默认调用约定,全称是C declaration。它的核心特点是参数从右向左压入栈中,由调用方负责清理栈空间,支持可变参数函数。
适用场景:需要支持可变参数的函数,比如printf类的格式化输出函数,或者需要跨编译器兼容的场景。
下面是一个使用__cdecl约定的函数示例:
// 显式指定__cdecl调用约定,默认情况下也可以不写
int __cdecl add(int a, int b) {
return a + b;
}
int main() {
int result = add(1, 2);
return 0;
}
对应的汇编层面逻辑大致如下:
; 调用方压栈参数,从右向左 push 2 push 1 ; 调用函数 call add ; 调用方清理栈,两个int参数共8字节,所以add esp,8 add esp,8
2. __stdcall 调用约定
__stdcall是Windows API常用的调用约定,全称是standard call。它的参数同样从右向左压入栈中,但是由被调用方负责清理栈空间,不支持可变参数。
适用场景:Windows系统API开发,或者不需要可变参数、希望减少调用方代码体积的场景。
示例代码:
// 显式指定__stdcall调用约定
int __stdcall multiply(int a, int b) {
return a * b;
}
int main() {
int result = multiply(3, 4);
return 0;
}
对应的汇编逻辑:
; 调用方压栈参数 push 4 push 3 call multiply ; 调用方不需要清理栈,栈清理在被调用函数内部完成
被调用函数multiply的结尾会有ret 8指令,直接清理两个int参数的栈空间。
3. __fastcall 调用约定
__fastcall是快速调用约定,它的核心特点是前两个不大于4字节的参数会通过寄存器(通常是ECX、EDX)传递,剩下的参数从右向左压入栈中,由被调用方负责清理栈空间。
适用场景:对性能要求较高的函数,减少栈操作的开销。
示例代码:
// 显式指定__fastcall调用约定
int __fastcall subtract(int a, int b) {
return a - b;
}
int main() {
int result = subtract(5, 3);
return 0;
}
对应的汇编逻辑:
; 前两个参数通过寄存器传递 mov ecx,5 mov edx,3 call subtract ; 调用方不需要清理栈
4. thiscall 调用约定
thiscall是C++类成员函数特有的调用约定,它没有显式的关键字修饰。参数从右向左压入栈中,this指针通过ECX寄存器传递,由被调用方负责清理栈空间。
适用场景:所有C++非静态类成员函数,是类成员函数的默认调用方式。
示例代码:
class Calculator {
public:
int add(int a, int b) {
return a + b;
}
};
int main() {
Calculator calc;
int result = calc.add(1, 2);
return 0;
}
对应的汇编逻辑中,调用add函数前会将calc对象的地址放入ECX寄存器,作为this指针传递。
不同调用约定的对比
我们可以通过表格更清晰地对比几种常见调用约定的差异:
| 调用约定 | 参数传递顺序 | 栈清理责任方 | 支持可变参数 | 适用场景 |
|---|---|---|---|---|
| __cdecl | 从右向左压栈 | 调用方 | 是 | 可变参数函数、跨编译器兼容 |
| __stdcall | 从右向左压栈 | 被调用方 | 否 | Windows API、固定参数函数 |
| __fastcall | 前两个参数用寄存器,其余压栈 | 被调用方 | 否 | 高性能要求的函数 |
| thiscall | 从右向左压栈,this用寄存器 | 被调用方 | 否 | C++类非静态成员函数 |
调用约定不匹配的问题
如果调用方和被调用方使用的调用约定不一致,会导致栈指针错误,引发程序崩溃或者数据错误。比如调用方用__cdecl约定调用一个声明为__stdcall的函数,调用方会尝试清理栈,而被调用方也已经清理了栈,就会导致栈不平衡。
我们在声明和定义函数时,尤其是跨模块(比如DLL导出函数)的场景,一定要保证调用约定一致。如果需要导出函数供其他语言调用,通常推荐使用__stdcall约定,因为它是Windows API的标准,兼容性更好。
总结
C++中的函数调用约定主要作用是规范函数调用的底层行为,不同的约定适配不同的开发场景。__cdecl适合可变参数场景,__stdcall适合Windows接口开发,__fastcall适合性能敏感场景,thiscall是类成员函数的默认约定。开发时需要根据实际需求选择合适的调用约定,尤其是跨模块调用时,必须保证调用双方约定一致,避免运行时错误。
C++函数调用约定__cdecl__stdcall__fastcall修改时间:2026-07-12 09:15:12