最近項目須要使用google test(如下簡稱爲gtest)做爲單元測試框架,可是項目自己過於龐大,main函數無從找起,須要將gtest框架編譯成靜態庫使用。由於項目自己是經過純c語言編寫,而gtest則是一個c++編寫的測試框架,其中必然涉及c與c++之間的相互調用。注意,本文的前提是,c代碼採用gcc等c語言編譯器編譯c代碼,採用g++等c++編譯器編譯c++代碼,若是c和c++代碼統一使用g++編譯,大部分狀況是能夠實現二者代碼相互調用的。如下爲踩坑過程的總結o_O||。ios
要了解二者之間如何實現相互調用,必須先了解c與c++之間的函數有什麼不一樣。c++
c++做爲c語言的升級版,二者必然有不少不一樣之處。其中有一個重大不一樣點就是,c++支持函數重載,而c語言不支持。爲了使函數支持重載,c++在c語言的基礎上,將函數名添加上返回值和參數的類型信息。例如,int add(int, int)
這個函數,經過c++編譯器編譯後,可能呈現的函數名爲int int_add_int_int(int, int)
(注:此處爲大概地說明c++是如何將返回值和參數信息添加到函數名中的,實際中編譯器不必定是這樣實現的)。框架
從以上說明能夠得出,因爲c++對函數重載的支持,使得編譯後的函數符號與c語言的不一致,即便是在二者函數名相同的前提下。函數
那麼,c與c++是不能相互調用了嗎?答案是否認的,由於存在着extern "C"
這個關鍵字可使語句能夠按照類C的編譯和鏈接規約來編譯和鏈接,而不是C++的編譯的鏈接規約。這樣在類C的代碼中就能夠調用C++的函數or變量等。單元測試
注意:extern "C"
指令中的"C",表示的一種編譯和鏈接規約,而不是一種語言。"C"表示符合C語言的編譯和鏈接規約的任何語言,如Fortran、assembler等。測試
還有要說明的是,extern "C"
指令僅指定編譯和鏈接規約,但不影響語義。例如在函數聲明中,指定了extern "C"
,仍然要遵照C++的類型檢測、參數轉換規則。google
對於c++,因爲c++的編譯器對c語言兼容,所以在c++中調用c語言編寫的函數,只須要在函數聲明前面加上關鍵字extern "C"
,表示採用類c語言的方式解析函數符號。例子以下:spa
// add.h #ifdef __ADD_H__ #define __ADD_H__ extern "C" int add(int a, int b); #endif // add.c int add(int a, int b) { return a + b; } // main.cc #include <iostream> #include "add.h" using namespace std; int main() { cout << "1 + 1 = " << add(1, 1) << endl; }
在例子中,main.cc爲c++代碼,add.c爲c語言代碼,當c++編譯器識別到extern "C"
`關鍵字時,會去尋找add函數的實現而不是尋找相似int_add_int_int這樣帶參數信息的函數實現。code
c語言調用c++代碼卻並不容易,緣由是c語言並不兼容c++。就算c語言能夠調用c++,也會由於沒法識別c++新定義的符號而編譯報錯。所以,爲了實現c語言調用c++函數,必須實現如下兩個步驟:1. 將c++相關函數封裝爲靜態庫或動態庫(由於調用庫函數時編譯器並不知道里面執行的是什麼語言);2. 對外提供遵循類c語言規約的接口函數。例子以下所示:接口
// printNum.h #ifdef __PRINTNUM_H__ #define __PRINTNUM_H__ extern "C" void printNum(int a); #endif // printNum.cc #include <iostream> #include "printNum.h" using namespace std; void printNum(int a) { cout << << "num is " << a << endl; } // main.c extern void printNum(int a); printNum(5);
經過將cout函數封裝爲類c語言規約的接口函數,使得main.c中能夠成功調用c++函數printNum。值得注意的是,main.c不能夠直接引入printNum.h,由於c語言不能識別extern "C"
關鍵字。能夠利用c++預約義宏實現頭文件的改寫:
#ifdef __PRINTNUM_H__ #define __PRINTNUM_H__ #ifdef __cplusplus extern "C" { #endif void printNum(int a); #ifdef __cplusplus } #endif #endif
小結以下:
extern "C"
關鍵字實現extern "C"
extern "C"
聲明的類c封裝函數