在C ++源代码中,extern“C”有什么作用?

究竟把extern "C"放到C ++代码中呢?

例如:

 extern "C" { void foo(); } 

extern“C”使C ++中的函数名具有'C'链接(编译器不会破坏名称),以便客户机C代码可以使用'C'兼容的头文件链接到(即使用)你的function的声明。 你的函数定义包含在一个二进制格式(由C ++编译器编译)中,客户端的“C”链接器将使用“C”名称链接到该格式。

由于C ++重载了函数名,而C没有,C ++编译器不能仅仅使用函数名作为唯一的id来链接,所以通过添加关于参数的信息来破坏名称。 AC编译器不需要修改名称,因为不能重载C中的函数名。当您声明函数在C ++中具有外部“C”链接时,C ++编译器不会将参数/参数types信息添加到用于连锁。

只要你知道,你可以明确地给每个单独的声明/定义指定“C”链接,或者使用一个块来将一系列声明/定义分组,以具有特定的链接:

 extern "C" void foo(int); extern "C" { void g(char); int i; } 

如果您关心的是技术问题,那么它们在C ++ 03标准的第7.5节中列出,这里是一个简短的摘要(重点放在extern“C”)上:

  • extern“C”是一个连接规范
  • 每个编译器都需要提供“C”链接
  • 链接规范只能在命名空间范围内进行
  • 所有函数types,函数名称和variables名称都有语言联系 见Richard的评论:只有函数名称和具有外部联接的variables名称具有语言联动
  • 具有不同语言联系的两种functiontypes是不同的types,即使其他方面相同
  • 链接规范嵌套,内部链接确定最终的链接
  • 对于class级成员,外部“C”被忽略
  • 最多一个具有特定名称的函数可以具有“C”连接(不pipe名称空间)
  • extern“C”强制一个函数有外部连接(不能使它静态) 见Richard的评论: 'extern'中的'static'C''是有效的; 如此宣布的实体具有内部联系,因此不具有语言联系
  • 从C ++到其他语言中定义的对象和其他语言中定义在C ++中的对象之间的链接是实现定义的和语言相关的。 只有两种语言实现的对象布局策略足够相似,才能实现这种连接

只是想添加一些信息,因为我还没有看到它发布。

你会经常看到C头文件中的代码,如下所示:

 #ifdef __cplusplus extern "C" { #endif // all of your legacy C code here #ifdef __cplusplus } #endif 

这个成就是它允许你在你的C ++代码中使用这个C头文件,因为macros“__cplusplus”将被定义。 但是,您仍然可以将其与您的遗留C代码(未定义该macros)一起使用,因此它不会看到唯一的C ++构造。

虽然,我也看到了C ++代码,如:

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

我想象完成了很多相同的事情。

不知道哪种方式更好,但我已经看到了。

在每个C ++程序中,所有非静态函数在二进制文件中都以符号表示。 这些符号是唯一标识程序中某个function的特殊文本string。

在C中,符号名称与函数名称相同。 这是可能的,因为在C中没有两个非静态函数可以具有相同的名称。

因为C ++允许重载并且具有许多C不具备的特性,比如类,成员函数,exception规范,所以不能简单地使用函数名作为符号名。 为了解决这个问题,C ++使用了所谓的名字修改,它将函数名和所有必要的信息(比如参数的数量和大小)转换成一些只有编译器知道的奇怪的string。

所以,如果你指定一个函数为extern C,那么编译器就不会执行名称修改,并且可以使用它的符号名直接访问它。

当使用dlsym()dlopen()来调用这样的函数时,这很方便。

我们来反编译生成的目标文件g ++ ,看看在这个实现里面发生了什么。

生成示例

input:

 void f() {} void g(); extern "C" { void ef() {} void eg(); } /* Prevent g and eg from being optimized away. */ void h() { g(); eg(); } 

编译GCC 4.8 Linux ELF输出:

 g++ -c a.cpp 

反编译符号表:

 readelf -s ao 

输出包含:

 Num: Value Size Type Bind Vis Ndx Name 8: 0000000000000000 6 FUNC GLOBAL DEFAULT 1 _Z1fv 9: 0000000000000006 6 FUNC GLOBAL DEFAULT 1 ef 10: 000000000000000c 16 FUNC GLOBAL DEFAULT 1 _Z1hv 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv 12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg 

解释

我们看到:

  • efeg被存储在与代码中的名字相同的符号中

  • 其他符号被打破了。 让我们打开它们:

     $ c++filt _Z1fv f() $ c++filt _Z1hv h() $ c++filt _Z1gv g() 

结论:以下两种符号types均未被破坏:

  • 定义
  • 声明但未定义( Ndx = UND ),在链接或运行时从另一个目标文件提供

所以在调用时你需要extern "C"

  • 来自C ++的C:告诉g++期望gcc产生的unmangled符号
  • 从C的C ++:告诉g++gcc使用生成unmangled符号

在外部不起作用的东西C

很显然,任何需要名称修饰的C ++特性都不会在extern C

 extern "C" { // Overloading. // error: declaration of C function 'void f(int)' conflicts with void f(); void f(int i); // Templates. // error: template with C linkage template <class C> void f(C i) { } } 

它改变了函数的链接,使得函数可以从C中调用。在实践中,这意味着函数名称不会被破坏 。

没有任何C头将编译与外部“C”。 当C头中的标识符与C ++关键字冲突时,C ++编译器会抱怨这一点。

例如,我看到以下代码在g ++中失败:

 extern "C" { struct method { int virtual; }; } 

有点有道理,但在将C代码移植到C ++时需要注意。

它通知C ++编译器在链接时以C风格查找这些函数的名称,因为在C和C ++中编译的函数的名称在链接阶段是不同的。

extern“C”意味着被C ++编译器识别,并通知编译器所指出的函数是以C风格编译(或将被编译)。 所以,当链接时,它链接到C的正确版本的function

C ++使用函数名称来从过程语言中创build一个面向对象的语言

大多数编程语言不是build立在现有编程语言之上的。 C ++是build立在C之上的,而且它是一个使用过程式编程语言构build的面向对象编程语言,所以有C ++关键字,例如extern ,它们提供了与C的向后兼容性。

我们来看下面的例子:

 #include <stdio.h> // Two functions are defined with the same name // but have different parameters void printMe(int a) { printf("int: %i\n", a); } void printMe(char a) { printf("char: %c\n", a); } int main() { printMe("a"); printMe(1); return 0; } 

AC编译器不会编译上面的例子,因为相同的函数printMe被定义了两次(即使它们有不同的参数int a vs char a )。

gcc -o printMe printMe.c && ./printMe;
1错误。 PrintMe被多次定义。

一个C ++编译器将编译上面的例子。 它不关心printMe被定义两次。

g ++ -o printMe printMe.c && ./printMe;

这是因为C ++编译器根据参数隐式重命名( mangles )函数。 在C中,这个function不被支持。 然而,当C ++构build于C之上时,语言被devise为面向对象的,并且需要支持使用同名的方法(函数)创build不同类的能力,并且基于不同的方法来覆盖方法( 方法重写 )参数。

Extern说:“不要破坏function名称”

但是,假设我们有一个名为“parent.c”的传统C文件,其中include来自其他传统C文件,“parent.h”,“child.h”等的函数名称。如果传统的“parent.c”文件是运行一个C ++编译器,那么函数名称将会被破坏,并且它们将不再匹配“parent.h”,“child.h”等中指定的函数名称,因此这些外部文件中的函数名称将需要也会被破坏。 这可能会变得相当混乱。 所以提供一个能告诉C ++编译器不要修改函数名的关键字是很方便的。

extern关键字告诉C ++编译器不要破坏(重命名)函数名称。 用法示例: extern void printMe(int a);

我之前使用'extern“C”'来创builddll(dynamic链接库)文件等main()函数“可导出”,以便稍后可以在dll中使用另一个可执行文件。 也许我以前用它的一个例子可能是有用的。

DLL

 #include <string.h> #include <windows.h> using namespace std; #define DLL extern "C" __declspec(dllexport) //I defined DLL for dllexport function DLL main () { MessageBox(NULL,"Hi from DLL","DLL",MB_OK); } 

可执行程序

 #include <string.h> #include <windows.h> using namespace std; typedef LPVOID (WINAPI*Function)();//make a placeholder for function from dll Function mainDLLFunc;//make a variable for function placeholder int main() { char winDir[MAX_PATH];//will hold path of above dll GetCurrentDirectory(sizeof(winDir),winDir);//dll is in same dir as exe strcat(winDir,"\\exmple.dll");//concentrate dll name with path HINSTANCE DLL = LoadLibrary(winDir);//load example dll if(DLL==NULL) { FreeLibrary((HMODULE)DLL);//if load fails exit return 0; } mainDLLFunc=(Function)GetProcAddress((HMODULE)DLL, "main"); //defined variable is used to assign a function from dll //GetProcAddress is used to locate function with pre defined extern name "DLL" //and matcing function name if(mainDLLFunc==NULL) { FreeLibrary((HMODULE)DLL);//if it fails exit return 0; } mainDLLFunc();//run exported function FreeLibrary((HMODULE)DLL); } 

extern "C"是一个链接规范,用于在Cpp源文件中 调用C函数 。 我们可以调用C函数,编写variables,并包含头文件 。 function在外部实体中声明,并在外部定义。 语法是

types1:

 extern "language" function-prototype 

types2:

 extern "language" { function-prototype }; 

例如:

 #include<iostream> using namespace std; extern "C" { #include<stdio.h> // Include C Header int n; // Declare a Variable void func(int,int); // Declare a function (function prototype) } int main() { func(int a, int b); // Calling function . . . return 0; } // Function definition . . . void func(int m, int n) { // // } 

当混合使用C和C ++(即,从C ++调用C函数;以及从C调用C ++函数)时,C ++名称会导致链接问题。 从技术上讲,只有当被调用函数已经被编译成二进制文件(很可能是一个* .a库文件)时,才会发生这个问题。

所以我们需要使用extern“C”来禁用C ++中的名字。