C/C++ 中有哪些不同的调用约定,每个约定是什么意思?

时间:2023-04-13
本文介绍了C/C++ 中有哪些不同的调用约定,每个约定是什么意思?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

在 C/C++ 中有不同的调用约定:stdcallexternpascal 等.有多少这样的调用约定可用,每个是什么意思?有没有描述这些的链接?

There are different calling conventions available in C/C++: stdcall, extern, pascal, etc. How many such calling conventions are available, and what do each mean? Are there any links that describe these?

推荐答案

简单回答:我使用 cdecl、stdcall 和 fastcall.我很少使用fastcall.stdcall 用于调用 Windows API 函数.

详细答案(从 维基百科窃取):

Detailed answer (Stolen from Wikipedia):

cdecl - 在 cdecl 中,子程序参数在堆栈上传递.整数值和内存地址在 EAX 寄存器中返回,浮点值在 ST0 x87 寄存器中返回.寄存器 EAX、ECX 和 EDX 是调用者保存的,其余的都是被调用者保存的.调用新函数时,x87 浮点寄存器 ST0 到 ST7 必须为空(弹出或释放),退出函数时 ST1 到 ST7 必须为空.不用于返回值时,ST0 也必须为空.

cdecl - In cdecl, subroutine arguments are passed on the stack. Integer values and memory addresses are returned in the EAX register, floating point values in the ST0 x87 register. Registers EAX, ECX, and EDX are caller-saved, and the rest are callee-saved. The x87 floating point registers ST0 to ST7 must be empty (popped or freed) when calling a new function, and ST1 to ST7 must be empty on exiting a function. ST0 must also be empty when not used for returning a value.

系统调用 - 这与 cdecl 类似,参数从右向左推送.EAX、ECX 和 EDX 不会被保留.双字中参数列表的大小在AL中传递.

syscall - This is similar to cdecl in that arguments are pushed right-to-left. EAX, ECX, and EDX are not preserved. The size of the parameter list in doublewords is passed in AL.

pascal - 参数按从左到右的顺序入栈(与 cdecl 相反),被调用者负责在返回前平衡栈.

pascal - the parameters are pushed on the stack in left-to-right order (opposite of cdecl), and the callee is responsible for balancing the stack before return.

stdcall - stdcall[4] 调用约定是 Pascal 调用约定的变体,其中被调用者负责清理堆栈,但参数被推入右侧的堆栈中-从左到右的顺序,就像在 _cdecl 调用约定中一样.寄存器 EAX、ECX 和 EDX 被指定在函数内使用.返回值存储在 EAX 寄存器中.

stdcall - The stdcall[4] calling convention is a variation on the Pascal calling convention in which the callee is responsible for cleaning up the stack, but the parameters are pushed onto the stack in right-to-left order, as in the _cdecl calling convention. Registers EAX, ECX, and EDX are designated for use within the function. Return values are stored in the EAX register.

fastcall - __fastcall 约定(又名 __msfastcall)传递适合 ECX 和 EDX 的前两个参数(从左到右计算).剩余的参数从右到左压入堆栈.

fastcall - __fastcall convention (aka __msfastcall) passes the first two arguments (evaluated left to right) that fit into ECX and EDX. Remaining arguments are pushed onto the stack from right to left.

vectorcall - 在 Visual Studio 2013 中,Microsoft 引入了 __vectorcall 调用约定,以应对游戏、图形、视频/音频和编解码器开发人员的效率问题.[7]对于 IA-32 和 x64 代码,__vectorcall 分别类似于 __fastcall 和原始 x64 调用约定,但对其进行了扩展以支持使用 SIMD 寄存器传递向量参数.对于 x64,当前六个参数中的任何一个是向量类型(float、double、__m128、__m256 等)时,它们通过相应的 XMM/YMM 寄存器传入.类似地,对于 IA-32,最多六个 XMM/YMM 寄存器从左到右依次分配给向量类型参数,而不管位置如何.此外,__vectorcall 增加了对传递同构向量聚合 (HVA) 值的支持,这些值是复合类型,仅由最多四个相同的向量类型组成,使用相同的六个寄存器.一旦为向量类型参数分配了寄存器,无论位置如何,未使用的寄存器都会从左到右分配给 HVA 参数.使用前四个 XMM/YMM 寄存器返回结果向量类型和 HVA 值.

vectorcall - In Visual Studio 2013, Microsoft introduced the __vectorcall calling convention in response to efficiency concerns from game, graphic, video/audio, and codec developers.[7] For IA-32 and x64 code, __vectorcall is similar to __fastcall and the original x64 calling conventions respectively, but extends them to support passing vector arguments using SIMD registers. For x64, when any of the first six arguments are vector types (float, double, __m128, __m256, etc.), they are passed in via the corresponding XMM/YMM registers. Similarly for IA-32, up to six XMM/YMM registers are allocated sequentially for vector type arguments from left to right regardless of position. Additionally, __vectorcall adds support for passing homogeneous vector aggregate (HVA) values, which are composite types consisting solely of up to four identical vector types, using the same six registers. Once the registers have been allocated for vector type arguments, the unused registers are allocated to HVA arguments from left to right regardless of position. Resulting vector type and HVA values are returned using the first four XMM/YMM registers.

safecall - Microsoft Windows 上的 n Delphi 和 Free Pascal,safecall 调用约定封装了 COM(组件对象模型)错误处理,因此异常不会泄露给调用者,而是在COM/OLE 要求的 HRESULT 返回值.从 Delphi 代码调用安全调用函数时,Delphi 还会自动检查返回的 HRESULT 并在必要时引发异常.

safecall - n Delphi and Free Pascal on Microsoft Windows, the safecall calling convention encapsulates COM (Component Object Model) error handling, thus exceptions aren't leaked out to the caller, but are reported in the HRESULT return value, as required by COM/OLE. When calling a safecall function from Delphi code, Delphi also automatically checks the returned HRESULT and raises an exception if necessary.

safecall 调用约定与 stdcall 调用约定相同,除了异常在 EAX 中作为 HResult(而不是在 FS:[0] 中)传递回调用者,而函数结果通过引用传递给堆栈就好像它是最终的输出"参数一样.当从 Delphi 调用一个 Delphi 函数时,这个调用约定将像任何其他调用约定一样出现,因为尽管异常在 EAX 中被传回,但它们会被调用者自动转换回正确的异常.使用其他语言创建的 COM 对象时,HResults 将作为异常自动引发,并且 Get 函数的结果在结果中而不是参数中.在 Delphi 中使用 safecall 创建 COM 对象时,无需担心 HResults,因为异常可以正常引发,但在其他语言中将被视为 HResults.

The safecall calling convention is the same as the stdcall calling convention, except that exceptions are passed back to the caller in EAX as a HResult (instead of in FS:[0]), while the function result is passed by reference on the stack as though it were a final "out" parameter. When calling a Delphi function from Delphi this calling convention will appear just like any other calling convention, because although exceptions are passed back in EAX, they are automatically converted back to proper exceptions by the caller. When using COM objects created in other languages, the HResults will be automatically raised as exceptions, and the result for Get functions is in the result rather than a parameter. When creating COM objects in Delphi with safecall, there is no need to worry about HResults, as exceptions can be raised as normal but will be seen as HResults in other languages.

Microsoft X64 调用约定 - Windows 上遵循 Microsoft x64 调用约定[12][13],并且预启动 UEFI(用于 x86-64 上的长模式).它使用寄存器 RCX、RDX、R8、R9 作为前四个整数或指针参数(按此顺序),而 XMM0、XMM1、XMM2、XMM3 用于浮点参数.额外的参数被压入堆栈(从右到左).如果 64 位或更少,则在 RAX 中返回整数返回值(类似于 x86).浮点返回值在 XMM0 中返回.长度小于 64 位的参数不进行零扩展;高位不归零.

Microsoft X64 Calling Convention - The Microsoft x64 calling convention[12][13] is followed on Windows and pre-boot UEFI (for long mode on x86-64). It uses registers RCX, RDX, R8, R9 for the first four integer or pointer arguments (in that order), and XMM0, XMM1, XMM2, XMM3 are used for floating point arguments. Additional arguments are pushed onto the stack (right to left). Integer return values (similar to x86) are returned in RAX if 64 bits or less. Floating point return values are returned in XMM0. Parameters less than 64 bits long are not zero extended; the high bits are not zeroed.

在 Windows 上下文中(无论是使用 Microsoft 还是非 Microsoft 工具)针对 x64 架构进行编译时,只有一种调用约定——此处描述的约定,因此 stdcall、thiscall、cdecl、fastcall 等都是现在都一样了.

When compiling for the x64 architecture in a Windows context (whether using Microsoft or non-Microsoft tools), there is only one calling convention – the one described here, so that stdcall, thiscall, cdecl, fastcall, etc., are now all one and the same.

在 Microsoft x64 调用约定中,调用者有责任在调用函数之前在堆栈上分配 32 字节的影子空间"(无论实际使用的参数数量如何),并在调用之后弹出堆栈称呼.阴影空间用于溢出 RCX、RDX、R8 和 R9,[14] 但必须可供所有函数使用,即使是那些参数少于四个的函数.

In the Microsoft x64 calling convention, it is the caller's responsibility to allocate 32 bytes of "shadow space" on the stack right before calling the function (regardless of the actual number of parameters used), and to pop the stack after the call. The shadow space is used to spill RCX, RDX, R8, and R9,[14] but must be made available to all functions, even those with fewer than four parameters.

寄存器 RAX、RCX、RDX、R8、R9、R10、R11 被认为是易失性的(调用者保存的).[15]

The registers RAX, RCX, RDX, R8, R9, R10, R11 are considered volatile (caller-saved).[15]

寄存器 RBX、RBP、RDI、RSI、RSP、R12、R13、R14 和 R15 被认为是非易失性的(被调用者保存的).[15]

The registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15 are considered nonvolatile (callee-saved).[15]

例如,一个接受 5 个整数参数的函数将在寄存器中取第一个到第四个,第五个将被推到阴影空间的顶部.所以当被调用的函数进入时,堆栈将由(升序)返回地址,后跟影子空间(32字节),后跟第五个参数组成.

For example, a function taking 5 integer arguments will take the first to fourth in registers, and the fifth will be pushed on the top of the shadow space. So when the called function is entered, the stack will be composed of (in ascending order) the return address, followed by the shadow space (32 bytes) followed by the fifth parameter.

在 x86-64 中,Visual Studio 2008 在 XMM6 和 XMM7(以及 XMM8 到 XMM15)中存储浮点数;因此,对于 x86-64,用户编写的汇编语言例程必须保留 XMM6 和 XMM7(与 x86 相比,其中用户编写的汇编语言例程不需要保留 XMM6 和 XMM7).换句话说,当从 x86 移植到 x86-64 时,必须更新用户编写的汇编语言例程,以在函数之前/之后保存/恢复 XMM6 和 XMM7.

In x86-64, Visual Studio 2008 stores floating point numbers in XMM6 and XMM7 (as well as XMM8 through XMM15); consequently, for x86-64, user-written assembly language routines must preserve XMM6 and XMM7 (as compared to x86 wherein user-written assembly language routines did not need to preserve XMM6 and XMM7). In other words, user-written assembly language routines must be updated to save/restore XMM6 and XMM7 before/after the function when being ported from x86 to x86-64.

这篇关于C/C++ 中有哪些不同的调用约定,每个约定是什么意思?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一篇:如何在 C++ 中按名称获取进程句柄? 下一篇:如何在 C++ 中检测 win32 进程的创建/终止

相关文章