C ++中extern“ C”的调用约定是什么?

时间:2019-02-28 06:03:05

标签: c++ c windows

标题实际上是对我所问问题的准确描述。

extern "C" int foo( int bar ) { return bar; }

根据我的测试,似乎不是__cdecl__stdcall__fastcall,显然不是__thiscall

约定是什么,它如何起作用?

谢谢。

3 个答案:

答案 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;
}

在单独的源文件中定义了func1func2func3的位置,以限制自动内联的可能性。

让我们看看为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调用中,编译后的函数负责将堆栈指针弹出回来。检查func1func2的程序集显示func1结尾于:

00211881  ret    // return, no stack adjustment

而func2以该程序集结尾:

002118E1  ret         4   // return and pop 4 bytes from stack

现在,在您断定“无链接属性”意味着“ __cdecl”之前,请记住Visual Studio项目具有以下设置:

enter image description here

让我们将“调用约定”设置更改为__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为默认值。例如驱动程序开发...