我正在学习编程语言课程,我们正在讨论extern "C"
声明。
除了“它接口C和C ++”之外,这个声明如何在更深层次上工作?这又如何影响程序中发生的绑定?
答案 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