C 和 C++ 之间的相互调用

旧文章搬家,转自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++ 库中含有类的,可以在二次接口函数中生成临时对象来调用对应的功能函数,当然要根据实际情况来定了。
comments powered by Disqus