旧文章搬家,转自Mike's Blog,留个备份。
extern "C"的理解
很多人认为 "C" 表示的 C 语言,实际并非如此,"C" 表示的是一种链接约定,只是因 C 和 C++ 语言之间的密切关系而在它们之间更多的应用而已。实际上 Fortran 和汇编语言也常常使用,因为它们也正好符合 C 实现的约定。
extern "C"指令描述的是一种链接约定,它并不影响调用函数的定义,即使做了该声明,对函数类型的检查和参数转换仍要遵循 C++ 的标准,而不是 C。
extern "C"的作用
不同的语言链接性是不同的,那么也决定了它们编译后的链接符号的不同,比如一个函数 void fun(double d),C 语言会把它编译成类似 _fun 这样的符号,C 链接器只要找到该函数符号就可以链接成功,它假设参数类型信息是正确的。而 C++ 会把这个函数编译成类似 _fun_double 或 _xxx_funDxxx 这样的符号,在符号上增加了类型信息,这也是 C++ 可以实现重载的原因。
那么,对于用 C 编译器编译成的库,用 C++ 直接链接势必会出现不能识别符号的问题,是的,需要extern "C" 的时刻来了,它就是干这个用的。extern "C" 的作用就是让编译器知道要以 C 语言的方式编译和链接封装函数。
在 C++ 中调用 C 库的例子
- 做一个C动态库
// hello.c:
void hello()
{
printf("hello\n");
}
- 编译并copy到系统库目录下 (也可以自己定义库目录,man ldconfig)
[root@coredump test]# gcc --shared -o libhello.so hello.c
[root@coredump test]# cp libhello.so /lib/
- 写个C++程序去调用它
#ifdef __cplusplus
extern "C" { // 告诉编译器下列代码要以C链接约定的模式进行链接
#endif
void hello();
#ifdef __cplusplus
}
#endif
int main()
{
hello();
return 0;
}
- 编译并运行
[root@coredump test]# g++ test.cpp -o test -lhello
[root@coredump test]# ./test
hello
[root@coredump test]#
C 调用 C++ 库
- 做一个C++库
void world()
{
std::cout << "world" << std::endl;
}
- 编译并copy到系统库目录下
[root@coredump test]# g++ --shared -o libworld.so world.cpp
[root@coredump test]# cp libworld.so /lib/
- 做一个中间接口库,对 C++ 库进行二次封装
void world();
#ifdef __cplusplus
extern "C" { // 即使这是一个C++程序,下列这个函数的实现也要以C约定的风格来搞!
#endif
void m_world()
{
world();
}
#ifdef __cplusplus
}
#endif
- 其中方法m_world即为libworld库中world方法的二次封装,编译并copy到系统库目录下
[root@coredump test]# g++ --shared -o libmid.so mid.cpp -lworld
[root@coredump test]# cp libmid.so /lib/
- C 程序通过链接二次接口库去调用 C++ 库
int main()
{
m_world();
return 0;
}
- 编译并运行
[root@coredump test]# gcc test.c -l mid -o test
[root@coredump test]# ./test
world
[root@coredump test]#
- 如果对于 C++ 库中含有类的,可以在二次接口函数中生成临时对象来调用对应的功能函数,当然要根据实际情况来定了。