C++ 中有哪些不同的函数调用约定?

来源:网站建设作者:闲进程头衔:程序员
导读:本期聚焦于小伙伴创作的《C++ 中有哪些不同的函数调用约定?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C++ 中有哪些不同的函数调用约定?》有用,将其分享出去将是对创作者最好的鼓励。

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

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

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。