在實際的工作中,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);
/* ... */
}