extern用法注意(轉)
用#include可以包含其他頭檔案中變量、函數的聲明,為什麼還要 extern關鍵字,如果我想引用一個全局變量或 函數a,我隻要直接在源檔案中包含#include<xxx.h> (xxx.h包含了a的聲明)不就可以了麼,為什麼還要用extern 呢??這個問題一直也是似是而非的困擾着我許多年了,今天上網狠狠查了一下總算小有 所獲了:
頭檔案
首先說下頭檔案,其實頭檔案對計算機而言沒什麼作用,她隻是在預編譯時在#include的地方展開一下,沒别的意義了,其實頭檔案主要是給别人看的。
我做過一個實驗,将頭檔案的字尾改成xxx.txt,然後在引用該頭檔案的地方用
#include"xxx.txt"
編譯,連結都很順利的過去了,由此可知,頭檔案僅僅為閱讀代碼作用,沒其他的作用了!
不管是C還是C++,你把你的函數,變量或者結構體,類啥的放在你的.c或者.cpp檔案裡。然後編譯成lib,dll,obj,.o等等,然後别人用的時候 最基本的gcc hisfile.cpp yourfile.o|obj|dll|lib 等等。
但對于我們程式員而言,他們怎麼知道你的lib,dll...裡面到底有什麼東西?要看你的頭檔案。你的頭檔案就是對使用者的說明。函數,參數,各種各樣的接口的說明。
那既然是說明,那麼頭檔案裡面放的自然就是關于函數,變量,類的“聲明”了。記着,是“聲明”,不是“定義”。
那麼,我假設大家知道聲明和定義的差別。是以,最好不要傻嘻嘻的在頭檔案裡定義什麼東西。比如全局變量:
#ifndef _XX_頭檔案.H
#define _XX_頭檔案.H
int A;
#endif
那麼,很糟糕的是,這裡的int A是個全局變量的定義,是以如果這個頭檔案被多次引用的話,你的A會被重複定義
顯 然文法上錯了。隻不過有了這個#ifndef的條件編譯,是以能保證你的頭檔案隻被引用一次,不過也許還是會岔子,但若多個c檔案包含這個頭檔案時還是會 出錯的,因為宏名有效範圍僅限于本c源檔案,是以在這多個c檔案編譯時是不會出錯的,但在連結時就會報錯,說你多處定義了同一個變量,
Linking...
incl2.obj : error LNK2005: "int glb" (?glb@@3HA) already defined in incl1.obj
Debug/incl.exe : fatal error LNK1169: one or more multiply defined symbols found
注意!!!
extern
這個關鍵字真的比較可惡,在聲明的時候,這個extern居然可以被省略,是以會讓你搞不清楚到底是聲明還是定義,下面分變量和函數兩類來說:
(1)變量
尤其是對于變量來說。
extern int a;//聲明一個全局變量a
int a; //定義一個全局變量a
extern int a =0 ;//定義一個全局變量a 并給初值。
int a =0;//定義一個全局變量a,并給初值,
第四個 等于 第 三個,都是定義一個可以被外部使用的全局變量,并給初值。
糊塗了吧,他們看上去可真像。但是定義隻能出現在一處。也就是說,不管是int a;還是extern int a=0;還是int a=0;都隻能出現一次,而那個extern int a可以出現很多次。
當你要引用一個全局變量的時候,你就要聲明,extern int a;這時候extern不能省略,因為省略了,就變成int a;這是一個定義,不是聲明。
(2)函數
函數,函數,對于函數也一樣,也是定義和聲明,定義的時候用extern,說明這個函數是可以被外部引用的,聲明的時候用extern說明這是一個聲明。 但由于函數的定義和聲明是有差別的,定義函數要有函數體,聲明函數沒有函數體,是以函數定義和聲明時都可以将extern省略掉,反正其他檔案也是知道這個函數是在其他地方定義的,是以不加extern也行。兩者如此不同,是以省略了extern也不會有問題。
比如:
int fun(void)
{
return 0;
}
很好,我們定義了一個全局函數
int fun(void);
我們對它做了個聲明,然後後面就可以用了
加不加extern都一樣
我們也可以把對fun的聲明 放在一個頭檔案裡,最後變成這樣
int fun(void);//函數聲明,是以省略了extern,完整些是extern int fun(void);
}//一個完整的全局函數定義,因為有函數體,extern同樣被省略了。
然後,一個客戶,一個要使用你的fun的客戶,把這個頭檔案包含進去,ok,一個全局的聲明。沒有問題。
但是,對應的,如果是這個客戶要使用全局變量,那麼要extern 某某變量;不然就成了定義了。