1 基本解釋
extern可以置于變量或者函數前,以标示變量或者函數的定義在别的檔案中,提示編譯器遇到此變量和函數時在其他子產品中尋找其定義。
另外,extern也可用來進行連結指定。
2 問題:extern 變量
在一個源檔案裡定義了一個數組:char a[6];
在另外一個檔案裡用下列語句進行了聲明:extern char *a;
請問,這樣可以嗎?
3 問題:extern 函數1
常常見extern放在函數的前面成為函數聲明的一部分,那麼,C語言的關鍵字extern在函數的聲明中起什麼作用?
答案與分析:
如果函數的聲明中帶有關鍵字extern,僅僅是暗示這個函數可能在别的源檔案裡定義,沒有其它作用。即下述兩個函數聲明沒有明顯的差別:
extern int f(); 和int f();
當然,這樣的用處還是有的,就是在程式中取代include “*.h”來聲明函數,在一些複雜的項目中,我比較習慣在所有的函數聲明前添加extern修飾。
答案與分析:
1)、不可以,程式運作時會告訴你非法通路。原因在于,指向類型T的指針并不等價于類型T的數組。extern char *a聲明的是一個指針變量而不是字元數組,是以與實際的定義不同,進而造成運作時非法通路。應該将聲明改為extern char a[ ]。
2)、例子分析如下,如果a[] = "abcd",則外部變量a=0x61626364 (abcd的ASCII碼值),*a顯然沒有意義
顯然a指向的空間(0x61626364)沒有意義,易出現非法記憶體通路。
3)、這提示我們,在使用extern時候要嚴格對應聲明時的格式,在實際程式設計中,這樣的錯誤屢見不鮮。
4)、extern用在變量聲明中常常有這樣一個作用,你在*.c檔案中聲明了一個全局的變量,這個全局的變量如果要被引用,就放在*.h中并用extern來聲明。
4 問題:extern 函數2
當函數提供方單方面修改函數原型時,如果使用方不知情繼續沿用原來的extern申明,這樣編譯時編譯器不會報錯。但是在運作過程中,因為少了或者多了輸入參數,往往會照成系統錯誤,這種情況應該如何解決?
答案與分析:
目前業界針對這種情況的處理沒有一個很完美的方案,通常的做法是提供方在自己的xxx_pub.h中提供對外部接口的聲明,然後調用方include該頭檔案,進而省去extern這一步。以避免這種錯誤。
寶劍有雙鋒,對extern的應用,不同的場合應該選擇不同的做法。
5 問題:extern “C”
在C++環境下使用C函數的時候,常常會出現編譯器無法找到obj子產品中的C函數定義,進而導緻連結失敗的情況,應該如何解決這種情況呢?
答案與分析:
C++語言在編譯的時候為了解決函數的多态問題,會将函數名和參數聯合起來生成一個中間的函數名稱,而C語言則不會,是以會造成連結時找不到對應函數的情況,此時C函數就需要用extern “C”進行連結指定,這告訴編譯器,請保持我的名稱,不要給我生成用于連結的中間函數名。
下面是一個标準的寫法:
//在.h檔案的頭上
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif
…
…
//.h檔案結束的地方
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
extern是C/C++語言中表明函數和全局變量 作用範圍(可見性)的關鍵字.
它告訴編譯器,其 聲明的函數和變量可以在本子產品或其它子產品中使用。
1。對于extern變量來說,僅僅是一個變量的聲明,其并不是在定義配置設定記憶體空間。如果該變量定義多次,會有連接配接錯誤
2。通常, 在子產品的頭檔案中對本子產品提供給其它子產品引用的函數和全局變量以關鍵字extern聲明。也就是說c檔案裡面定義,如果該函數或者變量與開放給外面,則在h檔案中用extern加以聲明。是以外部檔案隻用include該h檔案就可以了。而且編譯階段,外面是找不到該函數的,但是不報錯。link階段會從定義子產品生成的目标代碼中找到此函數。
3。與 extern對應的關鍵字是static,被它修飾的全局變量和函數隻能在本子產品中使用。
後面轉載,閱讀中。。。。。。。
被 extern "C"修飾的變量和函數是按照C語言方式編譯和連接配接的;
未加extern “C”聲明時的編譯方式
首先看看C++中對類似C的函數是怎樣編譯的。
作為一種面向對象的語言,C++支援函數重載,而過程式語言C則不支援。函數被C++編譯後在符号庫中的名字與C語言的不同。例如,假設某個函數的原型為:
void foo( int x, int y ); |
該函數被C編譯器編譯後在符号庫中的名字為_foo,而C++編譯器則會産生像_foo_int_int之類的名字(不同的編譯器可能生成的名字不同,但是都采用了相同的機制,生成的新名字稱為“mangled name”)。
_foo_int_int這樣的名字包含了函數名、函數參數數量及類型資訊,C++就是靠這種機制來實作函數重載的。例如,在C++中,函數void foo( int x, int y )與void foo( int x, float y )編譯生成的符号是不相同的,後者為_foo_int_float。
同樣地,C++中的變量除支援局部變量外,還支援類成員變量和全局變量。使用者所 編寫程式的類成員變量可能與全局變量同名,我們以"."來區分。而本質上,編譯器在進行編譯時,與函數的處理相似,也為類中的變量取了一個獨一無二的名字,這個名字與使用者程式中同名的全局變量名字不同。
未加extern "C"聲明時的連接配接方式
假設在C++中,子產品A的頭檔案如下:
// 子產品A頭檔案 moduleA.h #ifndef 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這樣的符号!
加extern "C"聲明後的編譯和連接配接方式
加 extern "C"聲明後,子產品A的頭檔案變為:
// 子產品A頭檔案 moduleA.h #ifndef MODULE_A_H #define MODULE_A_H extern "C" int foo( int x, int y ); #endif |
在子產品B的實作檔案中仍然調用foo( 2,3 ),其結果是:
(1)子產品A編譯生成foo的目标代碼時,沒有對其名字進行特殊處理,采用了C語言的方式;
(2)連接配接器在為子產品B的目标代碼尋找foo(2,3)調用時,尋找的是未經修改的符号名_foo。
如果在子產品A中函數聲明了foo為 extern "C"類型,而子產品B中包含的是 extern int foo( int x, int y ) ,則子產品B找不到子產品A中的函數;反之亦然。
是以,可以用一句話概括 extern “C”這個聲明的真實目的(任何語言中的任何文法特性的誕生都不是随意而為的,來源于真實世界的需求驅動。我們在思考問題時,不能隻停留在這個語言是怎麼做的,還要問一問它為什麼要這麼做,動機是什麼,這樣我們可以更深入地了解許多問題):
實作C++與C及其它語言的混合程式設計。
明白了C++中 extern "C"的設立動機,我們下面來具體分析 extern "C"通常的 使用技巧。
4. extern "C"的慣用法
(1)在C++中引用C語言中的函數和變量,在包含C語言頭檔案(假設為cExample.h)時,需進行下列處理:
extern "C" { #include "cExample.h" } |
而在C語言的頭檔案中,對其外部函數隻能指定為 extern類型,C語言中不支援 extern "C"聲明,在.c檔案中包含了 extern "C"時會出現編譯文法錯誤。
筆者編寫的C++引用C函數例子工程中包含的三個檔案的源代碼如下:
#ifndef C_EXAMPLE_H #define C_EXAMPLE_H extern int add(int x,int y); #endif #include "cExample.h" int add( int x, int y ) { return x + y; } // c++實作檔案,調用add:cppFile.cpp extern "C" { #include "cExample.h" } int main(int argc, char* argv[]) { add(2,3); return 0; } |
如果C++調用一個C語言編寫的.DLL時,當包括.DLL的頭檔案或聲明接口函數時,應加 extern "C" { }。
(2)在C中引用C++語言中的函數和變量時,C++的頭檔案需添加 extern "C",但是在C語言中不能直接引用聲明了 extern "C"的該頭檔案,應該僅将C檔案中将C++中定義的 extern "C"函數聲明為 extern類型。
筆者編寫的C引用C++函數例子工程中包含的三個檔案的源代碼如下:
//C++頭檔案 cppExample.h #ifndef CPP_EXAMPLE_H #define CPP_EXAMPLE_H extern "C" int add( int x, int y ); #endif //C++實作檔案 cppExample.cpp #include "cppExample.h" int add( int x, int y ) { return x + y; } extern int add( int x, int y ); int main( int argc, char* argv[] ) { add( 2, 3 ); return 0; } |