调用约定 calling-conventions
调用约定 | Microsoft Learn https://learn.microsoft.com/zh-cn/cpp/cpp/
Visual C/C++ 编译器提供了用于调用内部函数和外部函数的几个不同的约定。 了解这些不同的方法有助于调试程序以及将你的代码与汇编语言例程链接。
本主题中的各个主题说明了调用约定之间的差异、如何传递参数以及函数如何返回值。 它们也讨论了裸函数调用以及使你能够写入自己的 prolog 和 epilog 代码的高级功能。
若要了解 x64 处理器的调用约定,请参阅调用约定。
自变量传递和命名约定
Microsoft 专用
利用 Microsoft C++ 编译器,你可以指定在函数和调用方之间传递参数和返回值的约定。 并非所有约定都在所有支持的平台上可用,某些约定使用平台特定的实现。 在大多数情况下,将忽略在特定平台上指定不支持的约定的关键字或编译器开关,并将使用平台默认约定。
在 x86 平台上,所有参数在传递时都将加宽到 32 位。 返回值也将加宽到 32 位,并将通过 EAX 寄存器返回,但在 EDX:EAX 寄存器对中返回的 8 字节结构除外。 更大的结构将在 EAX 寄存器中作为指向隐藏返回结构的指针返回。 参数将从右到左推送到堆栈中。 不是 POD 的结构不会在寄存器中返回。
编译器将生成 prolog 和 epilog 代码来保存并还原 ESI、EDI、EBX 和 EBP 寄存器(如果在函数中使用了它们)。
备注
当结构、联合或类由值从函数返回时,类型的所有定义需要相同,否则程序可能在运行时失败。
有关如何定义自己的函数 prolog 和 epilog 代码的信息,请参阅裸函数调用。
有关面向 x64 平台的代码中的默认调用约定的信息,请参阅 x64 调用约定。 有关面向 ARM 平台的代码中的调用约定问题的信息,请参阅常见的 Visual C++ ARM 迁移问题。
Visual C/C++ 编译器支持下列调用约定。
关键字 | 堆栈清理 | 参数传递 |
---|---|---|
__cdecl | 调用方 | 在堆栈上按相反顺序推送参数(从右到左) |
__clrcall | 不适用 | 按顺序将参数加载到 CLR 表达式堆栈上(从左到右)。 |
__stdcall | 被调用方 | 在堆栈上按相反顺序推送参数(从右到左) |
__fastcall | 被调用方 | 存储在寄存器中,然后在堆栈上推送 |
__thiscall | 被调用方 | 在堆栈上推送;存储在 ECX 中的 this 指针 |
__vectorcall | 被调用方 | 存储在寄存器中,然后按相反顺序在堆栈上推送(从右到左) |
若要了解相关信息,请参阅已过时的调用约定。
Keyword | Stack cleanup | Parameter passing |
---|---|---|
__cdecl | Caller | Pushes parameters on the stack, in reverse order (right to left) |
__clrcall | n/a | Load parameters onto CLR expression stack in order (left to right). |
__stdcall | Callee | Pushes parameters on the stack, in reverse order (right to left) |
__fastcall | Callee | Stored in registers, then pushed on stack |
__thiscall | Callee | Pushed on stack; this pointer stored in ECX |
__vectorcall | Callee | Stored in registers, then pushed on stack in reverse order (right to left) |
For related information, see Obsolete Calling Conventions.