【黑馬程式員】————static和extern關鍵字1-對函數的作用

來源:互聯網
上載者:User

標籤:style   blog   http   color   使用   io   檔案   2014   

 一、extern與函數

如果一個程式中有多個源檔案(.c),編譯成功會產生對應的多個目標檔案(.obj),這些目標檔案還不能單獨運行,因為這些目標檔案之間可能會有關聯,比如a.obj可能會調用c.obj中定義的一個函數。將這些相關聯的目標檔案連結在一起後才能產生可執行檔。

先來理解2個概念:

  • 外部函數:如果在當前檔案中定義的函數允許其他檔案訪問、調用,就稱為外部函數。C語言規定,不允許有同名的外部函數。
  • 內建函式:如果在當前檔案中定義的函數不允許其他檔案訪問、調用,只能在內部使用,就稱為內建函式。C語言規定不同的源檔案可以有同名的內建函式,並且互不干擾。

接下來就示範在一個源檔案中調用另外一個源檔案定義的函數,比如在main.c中調用one.c中定義的one函數。

1.首先在one.c中定義了一個one函數

如果你想讓這個one函數可以被main.c訪問,那麼one函數就必須是外部函數。完整的定義是要加上extern關鍵字。

不過這個extern跟auto關鍵字一樣廢,完全可以省略,因為預設情況下,所有的函數就是外部函數。我們可以簡化一下:

 

2.接下來,我想在main.c的main函數中,調用one.c中的one函數

怎樣才能調用one.c中的one函數呢?你可能會產生2個想法:

想法1:直接在main函數中寫上one();

這個做法肯定不行,因為main函數根本不知道one函數的存在,怎麼調用呢?這個在標準C編譯器裡面會報錯的,但是在Xcode中只是個警告。

想法2:在main.c中包含one.c檔案

大家都知道#include的作用純粹就是內容拷貝,所以又相當於

哎,這麼一看好像是對的哦,在main函數前面定義了個one函數,然後在main函數中調用了這個one函數。從文法上看是對的,所以編譯是沒問題的。但是這個程式不可能運行成功,因為在連結的時候會報錯。我們已經在one.c中定義了one函數,現在又在main.c中定義one函數,C語言規定不允許有同名的外部函數,連結的時候連結器會發現在one.obj和main.obj中定義了同一個函數,會直接報錯,Xcode中的錯誤資訊是這樣的:

duplicate symbol _one是說one這個標識符重複了,linker是指連結器。

上面的2種想法都是不可行的,其實思路是一致的:讓main函數知道one函數的存在。正確的做法應該是在main函數前面對one函數進行提前聲明(看清楚,是聲明,不是定義,定義和聲明是兩碼事)。

3.在main函數前面對one函數進行提前聲明

你想要把其他源檔案中定義的外部函數拿過來聲明,完整的做法,應該使用extern關鍵字,表示引用別人的"外部函數"

運行程式,從控制台輸出可以發現 "one.c中定義的one函數" 已經被 "main.c的main函數" 成功調用了。

也有人可能會馬上冒出一個想法:假如除開one.c,還有其他源檔案也有定義這個one函數怎麼辦?那main函數調用的究竟是誰的one函數啊?放心,絕對不會有這種情況,剛才不是說了麼,不允許重複定義同一個外部函數,不然連結器會報錯的,所以只會有一個外部one函數。

上述就是extern關鍵字對函數的作用:用來定義和聲明一個外部函數。其實extern又跟auto一樣廢,完全可以省略。於是,我們可以簡化成這樣:

為了模組化地開發,在正規的項目裡面,我們會把one函數的聲明寫到另一個標頭檔中,當然,這個標頭檔的命名最好有意義、規範一點,比如叫one.h。以後,誰想調用這個one函數,包含one.h這個標頭檔就行了。於是最後的代碼結構是這樣的:

  

二、static與函數1.定義內建函式

從上面的例子可以看出,one.c中定義的one函數是可以被其他源檔案訪問的。其實有時候,我們可能想定義一個"內建函式",也就是不想讓其他檔案訪問本檔案中定義的函數。這個非常簡單,你只需要在定義函數的時候加個static關鍵字即可。

(我們就在上面例子的代碼基礎上進行修改)

我在void one()的前面加了個static,代表one函數是個內建函式。

然後你會發現程式運行不起來了,在連結的時候就報錯了。報錯的原因很簡單:我們在main.c中調用了one.c中定義的one函數,但是現在one.c的one函數是個"內建函式",不允許其他檔案訪問。我們來看看錯誤資訊:

第1個紅框中的Undefined symbols...意思是one這個標識符沒有被定義,也就是找不到one;第2個紅框的linker表明是連結器報錯了。

但這個程式是可以編譯成功的,因為我們在main函數前面聲明了one函數(函數的聲明和定義是兩碼事),這個函式宣告可以理解為:在文法上,騙一下main函數,告訴它one函數是存在的,所以從文法的角度上main函數是可以調用one函數的。究竟這個one函數存不存在呢,有沒有被定義呢?編譯器是不管的。在編譯階段,編譯器只會檢測單個源檔案的文法合不合理,並不檢測函數有沒有定義,只有在連結的時候才會檢測這個函數存不存在,也就是有沒有被定義。

  

我們再來討論一個問題,為什麼好多情況下都是可以成功編譯,但是連結的時候報錯呢?只要你理解編譯和連結的作用就好辦了。

所謂編譯,就是單獨檢查每個源檔案的文法是否合理,並不會檢查每個源檔案之間的關聯關係,一個源檔案編譯成功就產生一個目標檔案。

所謂連結,就是檢查目標檔案的關聯關係,將相關聯的目標檔案組合在一起,產生可執行檔。

看完這2個概念,再回去思考下前面報的錯,應該可以完全明白了。

2.聲明內建函式

我們還可以用static聲明一個內建函式

 1 #include <stdio.h> 2  3 static void test(); 4  5 int main(int argc, const char * argv[]) 6 { 7     test(); 8     return 0; 9 }10 11 static void test() {12     printf("調用了test函數");13 }

在第11行定義了一個test函數,這是一個內建函式,接著在第3行對test函數進行提前聲明,然後就可以在第7行可以調用test()函數了。

三、static、extern與函數的總結1.static

* 在定義函數時,在函數的最左邊加上static可以把該函式宣告為內建函式(又叫靜態函數),這樣該函數就只能在其定義所在的檔案中使用。如果在不同的檔案中有同名的內建函式,則互不干擾。

* static也可以用來聲明一個內建函式

 

2.extern

* 在定義函數時,如果在函數的最左邊加上關鍵字extern,則表示此函數是外部函數,可供其他檔案調用。C語言規定,如果在定義函數時省略extern,則隱含為外部函數。

* 在一個檔案中要調用其他檔案中的外部函數,則需要在當前檔案中用extern聲明該外部函數,然後就可以使用,這裡的extern也可以省略。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.