在 CDECL 参数以相反的顺序压入堆栈时,调用者清除堆栈并通过处理器注册表返回结果(稍后我将其称为“寄存器 A”)。在 STDCALL 中有一个区别,调用者不清除堆栈,而被调用者这样做。
你在问哪个更快。没有人。您应该尽可能使用本机调用约定。只有在没有出路的情况下才更改约定,当使用需要使用特定约定的外部库时。
此外,编译器可能会选择其他约定作为默认约定,即 Visual C++ 编译器使用 FASTCALL,理论上更快,因为处理器寄存器的使用范围更广。
通常,您必须为传递给某个外部库的回调函数提供正确的调用约定签名,即从 C 库对qsort 的回调必须是 CDECL(如果编译器默认使用其他约定,那么我们必须将回调标记为 CDECL)或者各种 WinAPI 回调必须是 STDCALL(整个 WinAPI 是 STDCALL)。
其他常见情况可能是当您存储指向某些外部函数的指针时,即创建指向 WinAPI 函数的指针,其类型定义必须用 STDCALL 标记。
下面是一个例子,展示了编译器是如何做到的:
/* 1. calling function in C++ */
i = Function(x, y, z);
/* 2. function body in C++ */
int Function(int a, int b, int c) { return a + b + c; }
CDECL:
/* 1. calling CDECL 'Function' in pseudo-assembler (similar to what the compiler outputs) */
push on the stack a copy of 'z', then a copy of 'y', then a copy of 'x'
call (jump to function body, after function is finished it will jump back here, the address where to jump back is in registers)
move contents of register A to 'i' variable
pop all from the stack that we have pushed (copy of x, y and z)
/* 2. CDECL 'Function' body in pseudo-assembler */
/* Now copies of 'a', 'b' and 'c' variables are pushed onto the stack */
copy 'a' (from stack) to register A
copy 'b' (from stack) to register B
add A and B, store result in A
copy 'c' (from stack) to register B
add A and B, store result in A
jump back to caller code (a, b and c still on the stack, the result is in register A)
标准调用:
/* 1. calling STDCALL in pseudo-assembler (similar to what the compiler outputs) */
push on the stack a copy of 'z', then a copy of 'y', then a copy of 'x'
call
move contents of register A to 'i' variable
/* 2. STDCALL 'Function' body in pseaudo-assembler */
pop 'a' from stack to register A
pop 'b' from stack to register B
add A and B, store result in A
pop 'c' from stack to register B
add A and B, store result in A
jump back to caller code (a, b and c are no more on the stack, result in register A)