C語言基礎-11-static和extern關鍵字1-對函數的做用

上一講中大體介紹了變量的類型,不一樣類型的變量有不一樣的存儲類型不一樣的生命週期不一樣的做用域。這講介紹2個比較重要的關鍵字:static和extern。html

static和extern不只能夠用在變量上,還能夠用在函數上。這講先介紹它們對函數的做用。ios

1、extern與函數

第三講第四講中,我提到過一句話:若是一個程序中有多個源文件(.c),編譯成功會生成對應的多個目標文件(.obj),這些目標文件還不能單獨運行,由於這些目標文件之間可能會有關聯,好比a.obj可能會調用c.obj中定義的一個函數。將這些相關聯的目標文件連接在一塊兒後才能生成可執行文件。模塊化

先來理解2個概念:函數

  • 外部函數:若是在當前文件中定義的函數容許其餘文件訪問、調用,就稱爲外部函數。C語言規定,不容許有同名的外部函數。spa

  • 內部函數:若是在當前文件中定義的函數不容許其餘文件訪問、調用,只能在內部使用,就稱爲內部函數。C語言規定不一樣的源文件能夠有同名的內部函數,而且互不干擾。code

接下來就演示在一個源文件中調用另一個源文件定義的函數,好比在main.c中調用one.c中定義的one函數。orm

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

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

不過這個extern跟auto關鍵字同樣廢,徹底能夠省略,由於默認狀況下,全部的函數就是外部函數。咱們能夠簡化一下:blog


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(這個是頭文件,之後調用該方法的時候直接包含該頭文件便可,就和OC裏邊的在頭文件裏邊先聲明方法,具體實現能夠在另外一個文件中實現相關方法)。之後,誰想調用這個one函數,包含one.h這個頭文件就好了。因而最後的代碼結構是這樣的:

  

 

2、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聲明一個內部函數

#include <stdio.h>

static void test();

int main(int argc, const char * argv[])
{
    test();
    return 0;
}

static void test() {
    printf("調用了test函數");
}

在第11行定義了一個test函數,這是一個內部函數,接着在第3行對test函數進行提早聲明,而後就能夠在第7行能夠調用test()函數了


3、static、extern與函數的總結

1.static

* 在定義函數時,在函數的最左邊加上static能夠把該函數聲明爲內部函數(又叫靜態函數),這樣該函數就只能在其定義所在的文件中使用。若是在不一樣的文件中有同名的內部函數,則互不干擾。

* static也能夠用來聲明一個內部函數

 

2.extern

* 在定義函數時,若是在函數的最左邊加上關鍵字extern,則表示此函數是外部函數,可供其餘文件調用。C語言規定,若是在定義函數時省略extern,則隱含爲外部函數。

* 在一個文件中要調用其餘文件中的外部函數,則須要在當前文件中用extern聲明外部函數,而後就可使用,這裏的extern也能夠省略。



注:本文轉自M了個J的博客。

相關文章
相關標籤/搜索