C 和 C + + 表面上是相似的,但是每个都编译成一组非常不同的代码。当使用 C + + 编译器包含头文件时,编译器期望使用 C + + 代码。然而,如果它是一个 C 头文件,那么编译器期望头文件中包含的数据被编译成某种格式ーー C + + “ ABI”,或者“应用二进制接口”,所以链接器会卡壳。这比将 C + + 数据传递给需要 C 数据的函数要好。
(为了深入了解真正的细节,C + + 的 ABI 通常会“混淆”它们的函数/方法的名称,所以调用 printf()时不会将原型标记为 C 函数,C + + 实际上会生成调用 _Zprintf的代码,并在最后添加额外的废话。)
因此: 在包含 c 头时使用 extern "C" {...}ーー就是这么简单。否则,编译后的代码就会不匹配,链接器就会卡住。然而,对于大多数头文件,您甚至不需要 extern,因为大多数系统 C 头文件已经说明了这样一个事实,即它们可能已经被 C + + 代码包含,并且已经被 extern "C"代码包含。
C 和 C + + 对于符号名称有不同的规则。符号是连接器如何知道对编译器生成的一个对象文件中的函数“ openBankAccount”的调用是对由同一(或兼容)编译器从不同源文件生成的另一个对象文件中的函数“ openBankAccount”的引用。这允许您使用多个源文件编写程序,这在处理大型项目时是一种解脱。
$ g++ -c test.C
$ nm test.o
U __gxx_personality_v0
0000000000000000 T foo
你得到了 C 连接。对象文件中的“ foo”函数的名称就是“ foo”,并且它没有来自名称错误处理的所有花哨的类型信息。
如果附带的代码是用 C 编译器编译的,但是您试图从 C + + 调用它,那么通常会在 exter“ C”{}中包含一个头。当您这样做时,您告诉编译器头中的所有声明都将使用 C 链接。当您链接代码时,您的。O 文件将包含对“ foo”的引用,而不包含对“ _ Z3foobra”的引用,这些引用希望能够匹配您链接到的库中的任何内容。
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
#ifndef C_H
#define C_H
/* This ifdef allows the header to be used from both C and C++. */
#ifdef __cplusplus
extern "C" {
#endif
int f();
#ifdef __cplusplus
}
#endif
#endif
#ifndef CPP_H
#define CPP_H
#ifdef __cplusplus
// C cannot see these overloaded prototypes, or else it would get confused.
int f(int i);
int f(float i);
extern "C" {
#endif
int f_int(int i);
int f_float(float i);
#ifdef __cplusplus
}
#endif
#endif
Cpp.cpp
#include "cpp.h"
int f(int i) {
return i + 1;
}
int f(float i) {
return i + 2;
}
int f_int(int i) {
return f(i);
}
int f_float(float i) {
return f(i);
}