專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

多文件EXTERN 變量和函數(shù)

作者:huqin   來源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時(shí)間:2014年08月15日   【字體:

用#include可以包含其他頭文件中變量、函數(shù)的聲明,為什么還要extern關(guān)鍵字,如果我想引用一個(gè)全局變量或函數(shù)a,我只要直接在源文件中包含#include (xxx.h包含了a的聲明)不就可以了么,為什么還要用extern呢??這個(gè)問題一直也是似是而非的困擾著我許多年了,今天上網(wǎng)狠狠查了一下總算小有所獲了:

 頭文件  首先說下頭文件,其實(shí)頭文件對(duì)計(jì)算機(jī)而言沒什么作用,她只是在預(yù)編譯時(shí)在#include的地方展開一下,沒別的意義了,其實(shí)頭文件主要是給別人看的。  我做過一個(gè)實(shí)驗(yàn),將頭文件的后綴改成xxx.txt,然后在引用該頭文件的地方用 #include"xxx.txt"  編譯,鏈接都很順利的過去了,由此可知,頭文件僅僅為閱讀代碼作用,沒其他的作用了!  不管是C還是C++,你把你的函數(shù),變量或者結(jié)構(gòu)體,類啥的放在你的.c或者.cpp文件里。然后編譯成lib,dll,obj,.o等等,然后別人用的時(shí)候 最基本的gcc hisfile.cpp yourfile.o|obj|dll|lib 等等。 但對(duì)于我們程序員而言,他們?cè)趺粗滥愕膌ib,dll...里面到底有什么東西?要看你的頭文件。你的頭文件就是對(duì)用戶的說明。函數(shù),參數(shù),各種各樣的接口的說明。     那既然是說明,那么頭文件里面放的自然就是關(guān)于函數(shù),變量,類的“聲明”了。記著,是“聲明”,不是“定義”。 那么,我假設(shè)大家知道聲明和定義的區(qū)別。所以,最好不要傻嘻嘻的在頭文件里定義什么東西。
比如全局變量: #ifndef _XX_頭文件.H #define _XX_頭文件.H int A; #endif  那么,很糟糕的是,這里的int A是個(gè)全局變量的定義,所以如果這個(gè)頭文件被多次引用的話,你的A會(huì)被重復(fù)定義 顯然語法上錯(cuò)了。只不過有了這個(gè)#ifndef的條件編譯,所以能保證你的頭文件只被引用一次,不過也許還是會(huì)岔子,但若多個(gè)c文件包含這個(gè)頭文件時(shí)還是會(huì)出錯(cuò)的,因?yàn)楹昝行Х秶鷥H限于本c源文件,所以在這多個(gè)c文件編譯時(shí)是不會(huì)出錯(cuò)的,但在鏈接時(shí)就會(huì)報(bào)錯(cuò),說你多處定義了同一個(gè)變量,    extern 這個(gè)關(guān)鍵字真的比較可惡,在聲明的時(shí)候,這個(gè)extern居然可以被省略,所以會(huì)讓你搞不清楚到底是聲明還是定義,下面分變量和函數(shù)兩類來說: 
(1)變量  尤其是對(duì)于變量來說。
 extern int a;//聲明一個(gè)全局變量a
 int a; //定義一個(gè)全局變量a 
 extern int a =0 ;//定義一個(gè)全局變量a 并給初值。
 nt a =0;//定義一個(gè)全局變量a,并給初值, 
第四個(gè) 等于 第 三個(gè),都是定義一個(gè)可以被外部使用的全局變量,并給初值。 糊涂了吧,他們看上去可真像。但是定義只能出現(xiàn)在一處。
也就是說,不管是int a;還是extern int a=0;還是int a=0;都只能出現(xiàn)一次,而那個(gè)extern int a可以出現(xiàn)很多次。  當(dāng)你要引用一個(gè)全局變量的時(shí)候,你就要聲明,extern int a;這時(shí)候extern不能省略,因?yàn)槭÷粤耍妥兂蒳nt a;這是一個(gè)定義,不是聲明。 
(2)函數(shù)      函數(shù),函數(shù),對(duì)于函數(shù)也一樣,也是定義和聲明,定義的時(shí)候用extern,說明這個(gè)函數(shù)是可以被外部引用的,聲明的時(shí)候用extern說明這是一個(gè)聲明。 但由于函數(shù)的定義和聲明是有區(qū)別的,定義函數(shù)要有函數(shù)體,聲明函數(shù)沒有函數(shù)體,所以函數(shù)定義和聲明時(shí)都可以將extern省略掉,反正其他文件也是知道這個(gè)函數(shù)是在其他地方定義的,所以不加extern也行。兩者如此不同,所以省略了extern也不會(huì)有問題。     比如: int fun(void) { return 0; }  很好,我們定義了一個(gè)全局函數(shù) int fun(void); 我們對(duì)它做了個(gè)聲明,然后后面就可以用了 加不加extern都一樣 我們也可以把對(duì)fun的聲明 放在一個(gè)頭文件里,最后變成這樣 int fun(void);//函數(shù)聲明,所以省略了extern,完整些是extern int fun(void); int fun(void) { return 0; }//一個(gè)完整的全局函數(shù)定義,因?yàn)橛泻瘮?shù)體,extern同樣被省略了。 然后,一個(gè)客戶,一個(gè)要使用你的fun的客戶,把這個(gè)頭文件包含進(jìn)去,ok,一個(gè)全局的聲明。沒有問題。 但是,對(duì)應(yīng)的,如果是這個(gè)客戶要使用全局變量,那么要extern 某某變量;不然就成了定義了。  總結(jié)下:  對(duì)變量而言,如果你想在本源文件中使用另一個(gè)源文件的變量,就需要在使用前用extern聲明該變量,或者在頭文件中用extern聲明該變量;  對(duì)函數(shù)而言,如果你想在本源文件中使用另一個(gè)源文件的函數(shù),就需要在使用前用聲明該變量,聲明函數(shù)加不加extern都沒關(guān)系,所以在頭文件中函數(shù)可以不用加extern。 
   (1) 處理時(shí)間:文件包含也是以"#"開頭來寫的(#include ), 那么它就是寫給預(yù)處理器來看了, 也就是說文件包含是會(huì)在編譯預(yù)處理階段進(jìn)行處理的。 
  (2) 處理方法:在預(yù)處理階段,系統(tǒng)自動(dòng)對(duì)#include命令進(jìn)行處理,具體做法是:降包含文件的內(nèi)容復(fù)制到包含語句(#include )處,得到新的文件,然后再對(duì)這個(gè)新的文件進(jìn)行編譯。  舉個(gè)很實(shí)際的例子,在單片機(jī)、ARM或其他嵌入式開發(fā)中,每一個(gè)平臺(tái)可能本身都有多種不同的硬件模塊,使用時(shí)需要去寫相應(yīng)的驅(qū)動(dòng)程序,這樣就可以把各個(gè)硬件模塊的驅(qū)動(dòng)程序作為一個(gè)模塊(比如lcd驅(qū)動(dòng)對(duì)對(duì)應(yīng)lcd.c和lcd.h,IIC驅(qū)動(dòng)對(duì)應(yīng)I2C.c和I2C.h等),當(dāng)具體使用到某個(gè)模塊時(shí),只需要在將對(duì)應(yīng)的.c和.h文件添加進(jìn)工程,并在文件中包含對(duì)就的.h文件即可。  
 所以關(guān)于頭文件的寫法個(gè)人總結(jié)以下幾點(diǎn):
 (1) 對(duì)應(yīng)的.c文件中寫變量、函數(shù)的定義
 (2) 對(duì)應(yīng)的.h文件中寫變量、函數(shù)的聲明
 (3) 如果有數(shù)據(jù)類型的定義 和 宏定義 ,請(qǐng)寫的頭文件(.h)中
 (4) 頭文件中一定加上#ifndef...#define....#endif之類的防止重包含的語句
(5) 模塊的.c文件中別忘包含自己的.h文件
關(guān)閉窗口

相關(guān)文章