extern "C"的主要做用就是爲了可以正確實現C++代碼調用其餘C語言代碼。加上extern "C"後,會指示編譯器這部分代碼按C語言(而不是C++)的方式進行編譯。因爲C++支持函數重載,所以編譯器編譯函數的過程當中會將函數的參數類型也加到編譯後的代碼中,而不只僅是函數名;而C語言並不支持函數重載,所以編譯C語言代碼的函數時不會帶上函數的參數類型,通常只包括函數名。html
這個功能十分有用處,由於在C++出現之前,不少代碼都是C語言寫的,並且很底層的庫也是C語言寫的,爲了更好的支持原來的C代碼和已經寫好的C語言庫,須要在C++中儘量的支持C,而extern "C"就是其中的一個策略。面試
這個功能主要用在下面的狀況:函數
看一個簡單的例子:.net
有moduleA、moduleB兩個模塊,B調用A中的代碼,其中A是用C語言實現的,而B是利用C++實現的,下面給出一種實現方法:設計
//moduleA頭文件 #ifndef __MODULE_A_H //對於模塊A來講,這個宏是爲了防止頭文件的重複引用 #define __MODULE_A_H int fun(int, int); #endif //moduleA實現文件moduleA.C //模塊A的實現部分並無改變 #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實現文件 moduleB.cpp //B模塊的實現也沒有改變,只是頭文件的設計變化了 #include"moduleB.h"
int main() { cout<<fun(2,3)<<endl; }
補充介紹:3d
因爲C、C++編譯器對函數的編譯處理是不徹底相同的,尤爲對於C++來講,支持函數的重載,編譯後的函數通常是以函數名和形參類型來命名的。unix
例如函數void fun(int, int),編譯後的多是_fun_int_int(不一樣編譯器可能不一樣,但都採用了相似的機制,用函數名和參數類型來命名編譯後的函數名);而C語言沒有相似的重載機制,通常是利用函數名來指明編譯後的函數名的,對應上面的函數可能會是_fun這樣的名字。htm
看下面的一個面試題:爲何標準頭文件都有相似的結構?blog
#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++語言中代表函數和全局變量的做用範圍的關鍵字,該關鍵字告訴編譯器,其申明的函數和變量能夠在本模塊或其餘模塊中使用。
記住,語句:extern int a; 僅僅是一個變量的聲明,其並非在定義變量a,也並未爲a分配空間。變量a在全部模塊中做爲一種全局變量只能被定義一次,不然會出錯。
一般來講,在模塊的頭文件中對本模塊提供給其餘模塊引用的函數和全局變量以關鍵字extern生命。例如,若是模塊B要引用模塊A中定義的全局變量和函數時只需包含模塊A的頭文件便可。這樣模塊B中調用模塊A中的函數時,在編譯階段,模塊B雖然找不到該函數,但並不會報錯;它會在連接階段從模塊A編譯生成的目標代碼中找到該函數。
extern對應的關鍵字是static,static代表變量或者函數只能在本模塊中使用,所以,被static修飾的變量或者函數不可能被extern C修飾。
上面也提到過,因爲C++支持函數重載,而C語言不支持,所以函數被C++編譯後在符號庫中的名字是與C語言不一樣的;C++編譯後的函數須要加上參數的類型才能惟一標定重載後的函數,而加上extern "C"後,是爲了向編譯器指明這段代碼按照C語言的方式進行編譯
未加extern "C"聲明時的連接方式:
//模塊A頭文件 moduleA.h #idndef _MODULE_A_H #define _MODULE_A_H int foo(int x, int y); #endif
在模塊B中調用該函數:
//模塊B實現文件 moduleB.cpp #include"moduleA.h" foo(2,3);
實際上,在連接階段,連接器會從模塊A生成的目標文件moduleA.obj中找_foo_int_int這樣的符號,顯然這是不可能找到的,由於foo()函數被編譯成了_foo的符號,所以會出現連接錯誤。
常見的作法能夠參考下面的一個實現(moduleA、moduleB兩個模塊,B調用A中的代碼,其中A是用C語言實現的,而B是利用C++實現):
//moduleA頭文件 #ifndef __MODULE_A_H //對於模塊A來講,這個宏是爲了防止頭文件的重複引用 #define __MODULE_A_H int fun(int, int); #endif //moduleA實現文件moduleA.C //模塊A的實現部分並無改變 #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實現文件 moduleB.cpp //B模塊的實現也沒有改變,只是頭文件的設計變化了 #include"moduleB.h" int main() { cout<<fun(2,3)<<endl; }
extern "C"的使用要點總結
extern "C" double sqrt(double);
extern "C" { double sqrt(double); int min(int, int); }
extern "C" { #i nclude <cmath> }
參考文章:
http://blog.chinaunix.net/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