为什么需要 extern "C" ?
转载来自:https://zhuanlan.zhihu.com/p/114669161
假设我们有下面三个文件:
// util.h int add(int, int);
// util.c int add(int a, int b) { return a + b; }
// main.c #include "util.h" int main(int argc, char *argv[]) { int sum = add(1, 2); return 0; }
使用 gcc 编译,生成的符号表内容分别是下面这个样子:
// main
// util.o
可以看到add函数在两个符号表中的符号都是_add.
我们再看看使用g++单独编译 main.c 文件,生成的main中符号表内容:
现在add函数在符号表中的符号是__Z3addii。这是由于g++编译器的Name mangling导致的。
为什么要name mangling?因为C++支持函数重载(函数名称相同,参数列表不同)。
需要为重载的函数生成唯一的符号表符号,以便链接器在链接的时候可以找到正确的函数地址。
我们尝试对add函数进行重载,在 main.c 中添加重载函数
```int add(double, double);``` #include "util.h" int add(double a, double b) { return a + b; } int main(int argc, char *argv[]) { int sum = add(1, 2); sum = add(1.0, 2.0); return 0; }
再次编译 main.c , 符号表发生了变化。相同名称的 add 函数,在经过 g++ 编译之后产生了两个不同的符号。
__Z3adddd,表示参数列表为两个double类型的add函数,(3代表函数名称是3个字符).
__Z3addii,表示参数列表为两个int类型的add函数。
由于gcc和g++生成符号表的方式不同,导致在C++项目中如果使用gcc编译的C模块,会出现链接错误。因为链接器会去C模块的(util.o)文件中查找,__Z3addii这样的符号。显然在C模块的目标文件(util.o)中,不存在__Z3addii这样的符号,它有的只是_add。最终导致链接错误,原因是”符号未定义“。 找不到”__Z3addii“,这个符号的定义。
那我们如果想在C++项目中使用gcc编译的C模块该怎么办呢?
// util.h
extern "C" { int add(int, int); }
通过extern "C",告诉g++编译器,不要对这些函数进行Name mangling,按照C编译器的方式去生成符号表符号。这样在main.c的目标文件(.o)中,参数列表为两个int类型的add函数名称为_add。链接器可以正确找到util.o中的add函数(他们都是_add)。注意参数列表为两个double类型的add函数名称还是__Z3adddd。
使用 extern ”C“ 的常见情况是使用第三方提供的编译好的静态链接库(.a/.lib),动态链接库(.so/.dll)。通常我们会拿到一个头文件和对应的编译好的库文件。
在头文件中通过条件编译引入 extern "C"。
#ifdef __cplusplus extern "C" { #endif int add(int, int); #ifdef __cplusplus } #endif
另外在C程序中调用C++函数也需要 extern "C" 的参与。