我正在学习编程语言课程,我们正在讨论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()
保留它的名字。
让我们看一个可以在 C 和 C++ 中编译的典型函数:
int Add (int a, int b)
{
return a+b;
}
现在在 C 中,该函数在内部称为“_Add”。而 C++ 函数在内部使用一个名为 name-mangling 的系统被称为完全不同的东西。它基本上是一种命名函数的方法,以便具有不同参数的相同函数具有不同的内部名称。
因此,如果在 add.c 中定义了 Add(),并且您在 add.h 中有原型,如果您尝试将 add.h 包含在 C++ 文件中,则会遇到问题。因为 C++ 代码正在寻找一个名称与 add.c 中的函数不同的函数,所以您将收到链接器错误。要解决该问题,您必须通过以下方法包含 add.c :
extern "C"
{
#include "add.h"
}
现在 C++ 代码将链接到 _Add 而不是 C++ 名称损坏的版本。
这是表达式的用途之一。底线,如果您需要在 C++ 程序中编译严格为 C 的代码(通过 include 语句或其他方式),您需要用 extern "C" { ... } 声明包装它。
当您使用 extern "C" 标记代码块时,您是在告诉系统使用 C 样式链接。
这主要影响链接器破坏名称的方式。您可以从链接器中获得标准的 C 风格命名,而不是使用 C++ 样式名称修饰(支持运算符重载更复杂)。
需要注意的是,extern "C"
还修改了函数的类型。它不仅修改了较低级别的内容:
extern "C" typedef void (*function_ptr_t)();
void foo();
int main() { function_ptr_t fptr = &foo; } // error!
of 的类型&foo
不等于 typedef 指定的类型(尽管代码被某些编译器接受,但不是所有编译器都接受)。
在 C++ 中,函数的名称/符号实际上被重命名为其他名称,以便不同的类/命名空间可以具有相同签名的函数。在 C 中,函数都是全局定义的,不需要这种自定义的重命名过程。
为了使 C++ 和 C 相互交流,“extern C”指示编译器不要使用 C 约定。
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++ 编译器会将源代码转换为目标文件中的不同形式:
例如,一个代码片段如下:
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