# externC
# 1.为什么要使用externC
一句话总结,C++支持函数重载,C语言不支持函数重载,在生成的C++编译文件中函数名会根据参数进行混淆(mangle),而C语言的编译文件不会被混淆,所以在C++程序中链接C语言的函数动态库时需要使用externC来保证函数签名的正确性。
/// main.cpp
void f() {}
void g();
extern "C" {
void ef() {}
void eg();
}
/* Prevent g and eg from being optimized away. */
void h() { g(); eg(); }
如上代码,使用命令编译后查看目标文件符号:
g++ -c -std=c++11 -Wall -Wextra -pedantic -o main.o main.cpp
readelf -s main.o
# Symbol table '.symtab' contains 13 entries:
# Num: Value Size Type Bind Vis Ndx Name
# 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
# 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS test_externC.cc
# 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
# 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
# 4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
# 5: 0000000000000000 0 SECTION LOCAL DEFAULT 6
# 6: 0000000000000000 0 SECTION LOCAL DEFAULT 7
# 7: 0000000000000000 0 SECTION LOCAL DEFAULT 5
# 8: 0000000000000000 7 FUNC GLOBAL DEFAULT 1 _Z1fv
# 9: 0000000000000007 7 FUNC GLOBAL DEFAULT 1 ef
# 10: 000000000000000e 17 FUNC GLOBAL DEFAULT 1 _Z1hv
# 11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv
# 12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND eg
可以看到被extern "C"
修饰的函数ef/gf
函数名没有被混淆mangle
,而未被函数名包围的按C++的编译规则被混淆了。
使用c++filt _Z1fv
命令可以反混淆(unmangle)函数名称,会发现_Z1fv/_Z1hv/_Z1gv
对应的就是f/g/h
c++filt
是一个用于解码(反编译)低级别的C++
和Java
符号名称的工具,用于将修饰后的函数名映射回源函数名。
# 使用externC的一个例子
先创建一个C
语言的so
库:
// add.h
int add(int x, int y);
// add.c
#include <add.h>
int add(int x, int y)
{
return x + y;
}
/// 编译成so库
/// gcc -fPIC -shared add.c -o libadd.so
通过上面的方式编译的so
文件中存在的函数符号名为add
,不包含参数名称修饰,因此无法直接被c++
程序链接
// main.cc
#include <add.h>
extern "C" {
printf(fmt, ...);
}
int main(int argc, char** argv)
{
int i = 1, j = 10;
printf("%d\n", add(i, j));
return 0;
}
/// gcc main.cc -o et -I ./ -L ./ -ladd
/// 报错
/// /usr/bin/ld: /tmp/ccxcRKM6.o: in function `main':
/// main.cc:(.text+0x2f): undefined reference to `add(int, int)'
/// collect2: error: ld returned 1 exit status
要想在c++
中使用C
语言编译的库,需要在C
语言库的头文件中告诉C++
哪些代码需要使用C
的方式来编译,上面例子将add.h
文件改成如下格式即可:
/// add.h
#ifdef __cplusplus
extern "C" {
#endif
int add(int x, int y);
#ifdef __cplusplus
}
#endif
如此就能正常编译运行了。
# reference
1.https://www.geeksforgeeks.org/extern-c-in-c/ (opens new window)
2.https://stackoverflow.com/questions/1041866/what-is-the-effect-of-extern-c-in-c (opens new window)