标题实际上是对我所问问题的准确描述。
extern "C" int foo( int bar ) { return bar; }
根据我的测试,似乎不是__cdecl
,__stdcall
,__fastcall
,显然不是__thiscall
。
约定是什么,它如何起作用?
谢谢。
答案 0 :(得分:2)
extern "C"
确定的全部是名称修饰。其他一切都取决于平台。
我只能假定您正在x86-64 / win64目标上进行测试?
如果是这样,那么所有这些调用约定将不再存在:
有关Win64,请参见https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2017。
搜索“ x86-64 System V ABI”。
所有指定呼叫约定的尝试都将被忽略,并使用统一的尝试。
对于x86,默认设置还是取决于平台,因此(过去)最好为具有多个选项的平台明确指定调用约定。
答案 1 :(得分:1)
x86 Disassembly/Calling Conventions上的Wikibook指出this有关C ++中extern "C"
的调用约定:
外部“ C”
在C ++源文件中,确保不要破坏放在外部“ C”块中的函数。当用C ++编写库时,经常会执行此操作,并且需要导出函数而不会使其混乱。即使该程序是用C ++编写并使用C ++编译器编译的,某些功能因此也可能不会被破坏,并且将使用一种普通的C调用约定(通常为CDECL)。
普通的C调用约定是CDECL,STDCALL和FASTCALL。
答案 2 :(得分:1)
让我们使用32位Visual Studio项目的Debug版本(默认设置)查看生成的程序集:
这是我的程序:
extern "C" int func1(int x);
extern "C" int __stdcall func2(int x);
extern "C" int __cdecl func3(int x);
int main()
{
int x = 0;
func1(1);
func2(2);
func3(2);
return 0;
}
在单独的源文件中定义了func1
,func2
和func3
的位置,以限制自动内联的可能性。
让我们看看为main生成的汇编代码:
func1(1);
002117E8 push 1
002117EA call _func1 (0211159h)
002117EF add esp,4
func2(2);
002117F2 push 2
002117F4 call _func2@4 (0211131h)
func3(3);
002117F9 push 3
002117FB call _func3 (021107Dh)
00211800 add esp,4
对于func1和func3,它是相同的签名。参数被推入堆栈,调用函数调用,然后将堆栈寄存器(esp)调回(弹出)到其先前的地址-如_cdecl
调用约定所期望的那样。在__cdecl调用约定中,调用者负责在进行函数调用后将堆栈指针恢复为其原始地址。
在调用func2
之后,没有堆栈指针调整。与声明的__stdcall调用约定一致。在__stdcall调用中,编译后的函数负责将堆栈指针弹出回来。检查func1
与func2
的程序集显示func1结尾于:
00211881 ret // return, no stack adjustment
而func2以该程序集结尾:
002118E1 ret 4 // return and pop 4 bytes from stack
现在,在您断定“无链接属性”意味着“ __cdecl”之前,请记住Visual Studio项目具有以下设置:
让我们将“调用约定”设置更改为__stdcall,看看生成的程序集是什么样的:
func1(1);
003417E8 push 1
003417EA call _func1@4 (034120Dh)
func2(2);
003417EF push 2
003417F1 call _func2@4 (0341131h)
func3(3);
003417F6 push 3
003417F8 call _func3 (034107Dh)
003417FD add esp,4
突然在调用func1之后main不会弹出参数-因此func1
假定了项目设置的默认调用约定。从技术上讲,这就是您的答案。
在某些环境中,__stdcall
为默认值。例如驱动程序开发...