在实际的工作中,C、C++密不可分,做我们嵌入式方面的,当然更多的是C,但,有时候却少不了C++,而且是C、C++混搭(混合编程)在一起的,比如,RTP视频传输,live555多媒体播放等都是C++下的,他需要调用JRTPLIB库,再比如,邮件发送,我也用C++写的,定义了一个Email对象,包含了成员:收发邮件地址,用户名,密码等,以及方法:邮件头、Base64编码和邮件发送这些操作,很好用,所以,很多时候,C++还是蛮不错的。。。。但,.c与.cpp文件混搭在一起,不是那么的简单,我们就来慢慢的了解吧。
C++之父在设计C++之时,考虑到当时已经存在了大量的C代码,为了支持原来的C代码和已经写好C库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。
一 extern "C"的作用
extern "C"的真实目的是实现类C和C++的混合编程。extern “C”是由C++提供的一个连接交换指定符号,用于告诉C++这段代码是C函数。extern “C”后面的函数不使用的C++的名字修饰,而是用C。这是因为C++编译后库中函数名会变得很长,与C生成的不一致,造成C++不能直接调用C函数。
C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:void foo(int x, int y);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。
被extern "C"限定的函数或变量是extern类型的;extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。被extern "C"修饰的变量和函数是按照C语言方式编译和连接的。
与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。
二 extern“C”与__cplusplus
#ifdef __cplusplus
extern "C" {
#endif
//....
#ifdef __cplusplus
}
#endif
__cplusplus是cpp中的自定义的宏,那么定义了这个宏的话表示这个文件是C++代码,也就是说,上面的代码的含义是:如果这是一段C++的代码,那么使用extern "C"来处理{ }之间的这段代码.即向C++编译器表明{ }之间的这段代码是C的代码.这是在C++的文件当中调用了C的代码.
三 C调用C++函数(接口)
若想在C中调用C++函数,则应该在C++的源文件中将这个函数声明为C风格的函数,即:
//CPP文件
extern "C" void f(void);
void f(void)
{
/*somecode*/
}
但是这种方法仅仅适用于非成员函数,若我们想要在C当中调用成员函数(包括虚函数),则需要提供一个简单的包装(wrapper).例如:
//C++ code
class C{
//...
virtual double f(int);
};
extern "C" double call_C_f(C *p,int i) //wrapper function
{
return p->f(i);
}
然后,你可以这样调用C::f();
//C code
double call_C_f(struct C *p ,int i);
void ccc(struct C *p,int i)
{
double = call_C_f(p,i);
/*...*/
}
如果你想在 C 里调用重载函数,则必须提供不同名字的包装,这样才能被 C 代码调用。例如:
// C++ code:
void f(int);
void f(double);
extern "C" void f_i(int i) { f(i); }
extern "C" void f_d(double d) { f(d); }
然后,你可以这样使用每个重载的 f():
/* C code: */
void f_i(int);
void f_d(double);
void cccc(int i,double d)
{
f_i(i);
f_d(d);
/* ... */
}