extern "C"的主要作用就是為了能夠正確實現(xiàn)C++代碼調(diào)用其他C語言代碼。加上extern "C"后,會指示編譯器這部分代碼按C語言的進行編譯,而不是C++的。由于C++支持函數(shù)重載,因此編譯器編譯函數(shù)的過程中會將函數(shù)的參數(shù)類型也加到編譯后的代碼中,而不僅僅是函數(shù)名;而C語言并不支持函數(shù)重載,因此編譯C語言代碼的函數(shù)時不會帶上函數(shù)的參數(shù)類型,一般之包括函數(shù)名。 這個功能十分有用處,因為在C++出現(xiàn)以前,很多代碼都是C語言寫的,而且很底層的庫也是C語言寫的,為了更好的支持原來的C代碼和已經(jīng)寫好的C語言庫,需要在C++中盡可能的支持C,而extern "C"就是其中的一個策略。 這個功能主要用在下面的情況: 1、C++代碼調(diào)用C語言代碼 2、在C++的頭文件中使用 3、在多個人協(xié)同開發(fā)時,可能有的人比較擅長C語言,而有的人擅長C++,這樣的情況下也會有用到 給出一個我設計的例子: moduleA、moduleB兩個模塊,B調(diào)用A中的代碼,其中A是用C語言實現(xiàn)的,而B是利用C++實現(xiàn)的,下面給出一種實現(xiàn)方法: //moduleA頭文件 #ifndef __MODULE_A_H //對于模塊A來說,這個宏是為了防止頭文件的重復引用 #define __MODULE_A_H int fun(int, int); #endif //moduleA實現(xiàn)文件moduleA.C //模塊A的實現(xiàn)部分并沒有改變 #include"moduleA" int fun(int a, int b) { return a+b; } //moduleB頭文件 #idndef __MODULE_B_H //很明顯這一部分也是為了防止重復引用 #define __MODULE_B_H #ifdef __cplusplus //而這一部分就是告訴編譯器,如果定義了__cplusplus(即如果是cpp文件, extern "C"{ //因為cpp文件默認定義了該宏),則采用C語言方式進行編譯 #include"moduleA.h" #endif … //其他代碼 #ifdef __cplusplus } #endif #endif //moduleB實現(xiàn)文件 moduleB.cpp //B模塊的實現(xiàn)也沒有改變,只是頭文件的設計變化了 #include"moduleB.h" int main() { cout<<fun(2,3)<<endl; } 下面是詳細的介紹: 由于C、C++編譯器對函數(shù)的編譯處理是不完全相同的,尤其對于C++來說,支持函數(shù)的重載,編譯后的函數(shù)一般是以函數(shù)名和形參類型來命名的。 例如函數(shù)void fun(int, int),編譯后的可能是(不同編譯器結(jié)果不同)_fun_int_int(不同編譯器可能不同,但都采用了類似的機制,用函數(shù)名和參數(shù)類型來命名編譯后的函數(shù)名);而C語言沒有類似的重載機制,一般是利用函數(shù)名來指明編譯后的函數(shù)名的,對應上面的函數(shù)可能會是_fun這樣的名字。 看下面的一個面試題:為什么標準頭文件都有類似的結(jié)構(gòu)? #ifndef __INCvxWorksh /*防止該頭文件被重復引用*/ #define __INCvxWorksh #ifdef __cplusplus //告訴編譯器,這部分代碼按C語言的格式進行編譯,而不是C++的 extern "C"{ #endif /*…*/ #ifdef __cplusplus } #endif #endif /*end of __INCvxWorksh*/ 分析:
#ifdef __cplusplus (其中__cplusplus是cpp中自定義的一個宏?。?!) extern "C"{ #endif #ifdef __cplusplus } #endif 的作用是什么呢? extern "C"包含雙重含義,從字面上可以知道,首先,被它修飾的目標是"extern"的;其次,被它修飾的目標代碼是"C"的。
extern是C/C++語言中表明函數(shù)和全局變量的作用范圍的關(guān)鍵字,該關(guān)鍵字告訴編譯器,其申明的函數(shù)和變量可以在本模塊或其他模塊中使用。 記住,下面的語句: extern int a; 僅僅是一個變量的聲明,其并不是在定義變量a,并未為a分配空間。變量a在所有模塊中作為一種全局變量只能被定義一次,否則會出錯。 通常來說,在模塊的頭文件中對本模塊提供給其他模塊引用的函數(shù)和全局變量以關(guān)鍵字extern生命。例如,如果模塊B要引用模塊A中定義的全局變量和函數(shù)時只需包含模塊A的頭文件即可。這樣模塊B中調(diào)用模塊A中的函數(shù)時,在編譯階段,模塊B雖然找不到該函數(shù),但并不會報錯;它會在鏈接階段從模塊A編譯生成的目標代碼中找到該函數(shù)。 extern對應的關(guān)鍵字是static,static表明變量或者函數(shù)只能在本模塊中使用,因此,被static修飾的變量或者函數(shù)不可能被extern C修飾。
上面也提到過,由于C++支持函數(shù)重載,而C語言不支持,因此函數(shù)被C++編譯后在符號庫中的名字是與C語言不同的;C++編譯后的函數(shù)需要加上參數(shù)的類型才能唯一標定重載后的函數(shù),而加上extern "C"后,是為了向編譯器指明這段代碼按照C語言的方式進行編譯 未加extern "C"聲明時的鏈接方式: //模塊A頭文件 moduleA.h #idndef _MODULE_A_H #define _MODULE_A_H int foo(int x, int y); #endif 在模塊B中調(diào)用該函數(shù): //模塊B實現(xiàn)文件 moduleB.cpp #include"moduleA.h" foo(2,3); 實際上,在鏈接階段,連接器會從模塊A生成的目標文件moduleA.obj中找_foo_int_int這樣的符號!??!,顯然這是不可能找到的,因為foo()函數(shù)被編譯成了_foo的符號,因此會出現(xiàn)鏈接錯誤。 常見的做法可以參考下面的一個實現(xiàn): moduleA、moduleB兩個模塊,B調(diào)用A中的代碼,其中A是用C語言實現(xiàn)的,而B是利用C++實現(xiàn)的,下面給出一種實現(xiàn)方法: //moduleA頭文件 #ifndef __MODULE_A_H //對于模塊A來說,這個宏是為了防止頭文件的重復引用 #define __MODULE_A_H int fun(int, int); #endif //moduleA實現(xiàn)文件moduleA.C //模塊A的實現(xiàn)部分并沒有改變 #include"moduleA" int fun(int a, int b) { return a+b; } //moduleB頭文件 #idndef __MODULE_B_H //很明顯這一部分也是為了防止重復引用 #define __MODULE_B_H #ifdef __cplusplus //而這一部分就是告訴編譯器,如果定義了__cplusplus(即如果是cpp文件, extern "C"{ //因為cpp文件默認定義了該宏),則采用C語言方式進行編譯 #include"moduleA.h" #endif … //其他代碼 #ifdef __cplusplus } #endif #endif //moduleB實現(xiàn)文件 moduleB.cpp //B模塊的實現(xiàn)也沒有改變,只是頭文件的設計變化了 #include"moduleB.h" int main() { cout<<fun(2,3)<<endl; } extern "C"的使用要點 1. 可以是單一語句 extern "C" double sqrt(double); 2. 可以是復合語句, 相當于復合語句中的聲明都加了extern "C" extern "C" { double sqrt(double); int min(int, int); } 3.可以包含頭文件,相當于頭文件中的聲明都加了extern "C" extern "C" { #i nclude <cmath> } 4. 不可以將extern "C" 添加在函數(shù)內(nèi)部 5. 如果函數(shù)有多個聲明,可以都加extern "C", 也可以只出現(xiàn)在第一次聲明中,后面的聲明會接受第一個鏈接指示符的規(guī)則。 6. 除extern "C", 還有extern "FORTRAN" 等。 參考文章: http://blog./u/29619/showart_230148.html http://blog.csdn.net/weiqubo/archive/2009/10/16/4681813.aspx http://hi.baidu.com/sundl2268/blog/item/4969453d2258bae53c6d970a.html |
|