外部“C”声明如何工作?

时间:2010-03-08 17:50:00

标签: c++ c extern-c

我正在学习编程语言课程,我们正在讨论extern "C"声明。

除了“它接口C和C ++”之外,这个声明如何在更深层次上工作?这又如何影响程序中发生的绑定?

9 个答案:

答案 0 :(得分:46)

extern "C"用于确保后面的符号不是mangled(已修饰)。


示例:

假设我们在名为test.cpp的文件中包含以下代码:

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

int bar() {
  return 1;
}

如果您运行gcc -c test.cpp -o test.o

看一下符号名称:

  

00000010 T _Z3barv

     

00000000 T foo

foo()保留其名称。

答案 1 :(得分:25)

让我们看一下可以在C和C ++中编译的典型函数:

int Add (int a, int b)
{
    return a+b;
}

现在在C中,该函数在内部被称为“_Add”。而使用名为name-mangling的系统在内部完全不同地调用C ++函数。它基本上是一种命名函数的方法,以便具有不同参数的相同函数具有不同的内部名称。

因此,如果在add.c中定义了Add(),并且你在add.h中有原型,那么如果你试图在一个C ++文件中包含add.h,你就会遇到问题。因为C ++代码正在寻找名称与add.c中的名称不同的函数,所以会出现链接器错误。要解决该问题,您必须通过此方法包含add.c:

extern "C"
{
#include "add.h"
}

现在,C ++代码将链接到_Add而不是C ++名称的错误版本。

这是表达式的一个用途。最重要的是,如果你需要在C ++程序中编译严格C的代码(通过include语句或其他方法),你需要用extern“C”{...}声明来包装它。

答案 2 :(得分:9)

当您使用extern“C”标记代码块时,您告诉系统使用C样式链接。

这主要影响链接器破坏名称的方式。您可以从链接器中获得标准的C风格命名,而不是使用C ++样式名称修改(支持运算符重载更复杂)。

答案 3 :(得分:5)

在C ++中,函数的名称/符号实际上被重命名为其他类,以便不同的类/名称空间可以具有相同签名的函数。在C中,函数全局定义,不需要这样的自定义重命名过程。

为了使C ++和C相互通信,“extern C”指示编译器不要使用C约定。

答案 4 :(得分:5)

应该注意extern "C"也修改了函数的类型。它不仅会修改较低级别的内容:

extern "C" typedef void (*function_ptr_t)();

void foo();

int main() { function_ptr_t fptr = &foo; } // error!

&foo的类型不等于typedef指定的类型(尽管代码被某些编译器接受,但并非所有编译器都接受)。

答案 5 :(得分:4)

extern C通过C ++编译器影响名称修改。它是一种让C ++编译器不会破坏名称的方法,或者更确切地说是以与C编译器相同的方式来破坏它们。这是它与C和C ++接口的方式。

举个例子:

extern "C" void foo(int i);

将允许该函数在C模块中实现,但允许从C ++模块调用它。

当试图让C模块调用C ++模块中定义的C ++函数(显然C不能使用C ++类)时,就会遇到麻烦。 C编译器不喜欢extern "C"

所以你需要使用它:

#ifdef __cplusplus
extern "C" {
#endif

void foo(int i);

#ifdef __cplusplus
}
#endif

现在,当它出现在头文件中时,C和C ++编译器都会对声明感到满意,现在它可以在C或C ++模块中定义,并且可以由C和C ++代码调用。 / p>

答案 6 :(得分:3)

extern“C”表示附带的代码使用C风格的链接和名称修改。 C ++使用更复杂的名称修改格式。这是一个例子:

http://en.wikipedia.org/wiki/Name_mangling

int example(int alpha, char beta);
C中的

_example

在C ++中:__Z7exampleic

更新:正如GManNickG在评论中指出的那样,名称修改模式依赖于编译器。

答案 7 :(得分:0)

extern" C",是一个用C绑定声明函数的关键字,因为C编译器和C ++编译器会将源代码转换为目标文件中的不同形式:

例如,代码段如下:

int _cdecl func1(void) {return 0}
int _stdcall func2(int) {return 0}
int _fastcall func3(void) {return 1}

32位C编译器将翻译表单中的代码,如下所示:

_func1
_func2@4
@func3@4
cdecl中的

,func1将翻译为' _name '

在stdcall中,func2将翻译为' _name @ X '

在fastcall中,func2将翻译为' @ name @ X '

&#39; <强> X &#39;表示参数列表中参数的字节数。

Windows上的64位约定没有前导下划线

在C ++中,引入了类,模板,命名空间和运算符重载,因为不允许两个具有相同名称的函数,C ++编译器在符号名称中提供类型信息,

例如,代码段如下:

int func(void) {return 1;}
int func(int) {return 0;}
int func_call(void) {int m=func(), n=func(0);}

C ++编译器将按如下方式转换代码:

int func_v(void) {return 1;}
int func_i(int) {return 0;}
int func_call(void) {int m=_func_v(), n=_func_i(0);}

&#39; _v&#39;和&#39; _i&#39;是&#39; void&#39;的类型信息。和&#39; int&#39;

答案 8 :(得分:-2)

以下是msdn

的引用

“extern关键字声明一个变量或函数,并指定它具有外部链接(其名称可以从其定义的文件以外的文件中看到)。修改变量时,extern指定变量具有静态持续时间(它在程序开始时分配,并在程序结束时解除分配。)变量或函数可以在另一个源文件中定义,或者稍后在同一个文件中定义。默认情况下,文件范围内的变量和函数声明是外部的。“ p>

http://msdn.microsoft.com/en-us/library/0603949d%28VS.80%29.aspx