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

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

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

extern "C"用于确保后面的符号不受损坏 (修饰)。


例:

假设我们在名为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()保留其名称。

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

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

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

因此,如果在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”{…}声明来包装它。

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

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

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

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

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

 extern "C" typedef void (*function_ptr_t)(); void foo(); int main() { function_ptr_t fptr = &foo; } // error! 

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

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 ++代码调用。

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

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

 int example(int alpha, char beta); 

在C: _example

在C ++中: __Z7exampleic

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

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

例如,代码段如下:

 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

X ‘表示参数列表中参数的字节数。

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);} 

‘_v’和’_i’是’void’和’int’的类型信息

这是msdn的引用

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

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