首先,分析下面的代碼片段:
- // Demo.h
- #ifndef SRC_DEMO_H
- #define SRC_DEMO_H
- extern "C"
- {
- ... // do something
- }
- #endif // SRC_DEMO_H
顯然,頭文件中的編譯宏“#ifndef SRC_DEMO_H、#define SRC_DEMO_H、#endif”的作用是防止該頭文件被重復引用。那麼,extern "C"又有什麼特殊的作用呢?暫且先留著這個疑問。
C++語言被稱做“C with classes”、“a better C”或“C的超集合”,但是並非兼容C語言的所有東西,兩者之間的“大同”並不能完全抹殺其中的“小異”。最常見的差異就是,C允許從void類型指針隱式轉換成其他類型的指針,但C++為了安全考慮明令禁止了此種行為。比如:如下代碼在C語言中是有效的:
但要使其在C++中正確運行,就需要顯式地轉換:
- // 從void* 隱式轉換為double*
- double *pDouble = malloc(nCount * sizeof(double));
- double *pDouble = (double *)malloc(nCount * sizeof(double));
除此之外,還有一些其他的可移植問題,比如new和class在 C++中是關鍵字,而在C中,卻可以作為變量名。
若想在C++中使用大量現成的C程序庫,就必須把它放到extern "C" { /* code */ }中。到這裡,也許大家會茅塞頓開,明白本建議開始列出的代碼片段中那些宏的真實作用了。當然,具有強烈好奇心的讀者也許會有了新的問題:為什麼加上extern "C" { /* code */ }就好使了呢?這是一個問題。下面就分析一下隱藏在這個現象背後的真實原因:C與C++具有不同的編譯和鏈接方式。C編譯器編譯函數時不帶函數的類型信息,只包含函數符號名字;而C++編譯器為了實現函數重載,在編譯時會帶上函數的類型信息。假設某個函數的原型為:
- int Function(int a, float b);
C編譯器把該函數編譯成類似_ Function的符號(這種符號一般被稱為mangled name),C鏈接器只要找到了這個符號,就可以連接成功,實現調用。C編譯鏈接器不會對它的參數類型信息加以驗證,只是假設這些信息是正確的,這正是C編譯鏈接器的缺點所在。而在強調安全的C++中,編譯器會檢查參數類型信息,上述函數原型會被編譯成_ Function_int_float這樣的符號(也正是這種機制為函數重載的實現提供了必要的支持)。在連接過程中,鏈接器會在由函數原型所在模塊生成的目標文件中尋找_ Function_int_float這樣的符號。
解決上述矛盾就成了設置extern "C"這一語法最直接的原因與動力。extern "C"的作用就是告訴C++鏈接器尋找調用函數的符號時,采用C的方式,讓編譯器尋找_ Function而不是_ Function_int_float。
要實現在C++中調用C的代碼,具體方式有以下幾種:
(1)修改C代碼的頭文件,當其中含有C++代碼時,在聲明中加入extern "C"。代碼如下所示:
(2)在C++代碼中重新聲明一下C函數,在重新聲明時添加上extern "C"。代碼如下所示:
- /*C語言頭文件:CDemo.h */
- #ifndef C_SRC_DEMO_H
- #define C_SRC_DEMO_H
- extern "C" int Function(int x,int y);
- #endif // C_SRC_DEMO_H
- /* C語言實現文件:CDemo.c */
- #include " CDemo.h"
- int Function ( int x, int y )
- {
- ... // processing code
- }
- // C++調用文件
- #include " CDemo.h"
- int main()
- {
- Function (2,3);
- return 0;
- }
(3)在包含C頭文件時,添上extern "C"。代碼如下所示:
- /*C語言頭文件:CDemo.h */
- #ifndef C_SRC_DEMO_H
- #define C_SRC_DEMO_H
- extern int Function(int x,int y);
- #endif // C_SRC_DEMO_H
- /* C語言實現文件:CDemo.c */
- #include "CDemo.h"
- int Function ( int x, int y )
- {
- ... // processing code
- }
- // C++調用文件
- #include "CDemo.h"
- extern "C" int Function(int x,int y);
- int main()
- {
- Function (2,3);
- return 0;
- }
- /*C語言頭文件:CDemo.h */
- #ifndef C_SRC_DEMO_H
- #define C_SRC_DEMO_H
- extern int Function(int x,int y);
- #endif // C_SRC_DEMO_H
- /* C語言實現文件:CDemo.c */
- #include "CDemo.h"
- int Function ( int x, int y )
- {
- ... // processing code
- }
- // C++調用文件
- extern "C" {
- #include "CDemo.h"
- }
- int main()
- {
- Function (2,3);
- return 0;
- }
使用中,謹記: extern "C"一定要加在C++的代碼文件中才能起作用。
請記住:
若想在C++中使用大量現成的C程序庫,實現C++與C的混合編程,那你必須了解extern "C"是怎麼回事兒,明白extern "C"的使用方式。