外部“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