C學習-預處理指令-static和extern關鍵字-對函數、變量的做用(七)

咱們知道,不一樣類型的變量有不一樣的存儲類型、不一樣的生命週期、不一樣的做用域。這講介紹2個比較重要的關鍵字:staticextern。static和extern不只能夠用在變量上,還能夠用在函數上。html

1、變量類型

C語言根據變量的存儲類型的不一樣,能夠把變量分爲:自動變量靜態變量寄存器變量`。
自動變量是存儲在堆棧中的。ios

靜態變量是存儲在靜態內存中的,也就是不屬於堆棧。模塊化

哪些是靜態變量:函數

  • 全部的全局變量都是靜態變量spa

  • 被關鍵字static修飾的局部變量也是靜態變量code

生命週期:靜態變量在程序運行以前建立,在程序的整個運行期間始終存在,直到程序結束。htm

2、extern與函數

咱們知道:若是一個程序中有多個源文件(.c),編譯成功會生成對應的多個目標文件(.obj),這些目標文件還不能單獨運行,由於這些目標文件之間可能會有關聯,好比a.obj可能會調用c.obj中定義的一個函數。將這些相關聯的目標文件連接在一塊兒後才能生成可執行文件。blog

先來理解2個概念:生命週期

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

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

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

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

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

clipboard.png

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

clipboard.png

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

clipboard.png

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

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

clipboard.png

這個作法確定不行,由於main函數根本不知道one函數的存在,怎麼調用呢?這個在標準C編譯器裏面會報錯的,可是在Xcode中只是個警告。

想法2:在main.c中包含one.c文件

clipboard.png

你們都知道#include的做用純粹就是內容拷貝,因此又至關於

clipboard.png
哎,這麼一看好像是對的哦,在main函數前面定義了個one函數,而後在main函數中調用了這個one函數。從語法上看是對的,因此編譯是沒問題的。可是這個程序不可能運行成功,由於在連接的時候會報錯。咱們已經在one.c中定義了one函數,如今又在main.c中定義one函數,C語言規定不容許有同名的外部函數,連接的時候連接器會發如今one.obj和main.obj中定義了同一個函數,會直接報錯,Xcode中的錯誤信息是這樣的:

clipboard.png

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

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

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

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

clipboard.png

運行程序,從控制檯輸出能夠發現 "one.c中定義的one函數" 已經被 "main.c的main函數" 成功調用了。

也有人可能會立刻冒出一個想法:假如除開one.c,還有其餘源文件也有定義這個one函數怎麼辦?那main函數調用的到底是誰的one函數啊?放心,絕對不會有這種狀況,剛纔不是說了麼,不容許重複定義同一個外部函數,否則連接器會報錯的,因此只會有一個外部one函數。

上述就是extern關鍵字對函數的做用:用來定義和聲明一個外部函數。其實extern又跟auto同樣廢,徹底能夠省略。因而,咱們能夠簡化成這樣:

clipboard.png

爲了模塊化地開發,在正規的項目裏面,咱們會把one函數的聲明寫到另外一個頭文件中,固然,這個頭文件的命名最好有意義、規範一點,好比叫one.h。之後,誰想調用這個one函數,包含one.h這個頭文件就好了。因而最後的代碼結構是這樣的:

clipboard.png

clipboard.png

clipboard.png

3、static與函數

1.定義內部函數

從上面的例子能夠看出,one.c中定義的one函數是能夠被其餘源文件訪問的。其實有時候,咱們可能想定義一個"內部函數",也就是不想讓其餘文件訪問本文件中定義的函數。這個很是簡單,你只須要在定義函數的時候加個static關鍵字便可。

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

clipboard.png

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

而後你會發現程序運行不起來了,在連接的時候就報錯了。報錯的緣由很簡單:咱們在main.c中調用了one.c中定義的one函數,可是如今one.c的one函數是個"內部函數",不容許其餘文件訪問。咱們來看看錯誤信息:

clipboard.png

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

但這個程序是能夠編譯成功的,由於咱們在main函數前面聲明瞭one函數(函數的聲明和定義是兩碼事),這個函數聲明能夠理解爲:在語法上,騙一下main函數,告訴它one函數是存在的,因此從語法的角度上main函數是能夠調用one函數的。究竟這個one函數存不存在呢,有沒有被定義呢?編譯器是無論的。在編譯階段,編譯器只會檢測單個源文件的語法合不合理,並不檢測函數有沒有定義,只有在連接的時候纔會檢測這個函數存不存在,也就是有沒有被定義。

clipboard.png

clipboard.png

咱們再來討論一個問題,爲何好多狀況下都是能夠成功編譯,可是連接的時候報錯呢?只要你理解編譯和連接的做用就好辦了。

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

所謂連接就是檢查目標文件的關聯關係,將相關聯的目標文件組合在一塊兒,生成可執行文件

看完這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()函數了

4、static、extern與函數的總結

1.static

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

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

2.extern

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

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

5、對變量的做用

extern 關鍵字對變量的做用

一、不一樣源文件中的同名變量

前面講到,你在一個源文件中不管寫多少遍全局變量int a;,它們表明的都是同一個變量。還有一個事實,假如在另外一個源文件中也有全局變量int a;,那麼這兩個源文件的全部全局變量int a;都表明着同一個變量。extern關鍵字仍是適用的,好比:

clipboard.png
clipboard.png

或者是:

clipboard.png
clipboard.png

上面的兩種狀況下,test.c和main.c中使用的全局變量a都仍是表明着同一個變量

注意了,不能夠兩個文件的全部所有變量a都用extern,下面的作法是錯誤的:

clipboard.png
clipboard.png

由於extern是用來聲明一個已經定義過的變量這兩個文件都是在聲明變量,沒有人定義變量,在連接的時候確定報錯

clipboard.png

大體錯誤意思是:標示符a未定義

static 關鍵字對變量的做用

但不少時候,咱們並不想讓源文件中的全局變量跟其餘源文件共享,至關於私有的全局變量,那麼你就得用static關鍵字來定義變量。

clipboard.png

clipboard.png

這樣寫完,test.c和main.c的變量a分別表明着不一樣的變量,它們是沒有聯繫的、互不干擾的。也就是說,main.c沒法訪問test.c中的變量a,所以在main.c中將a修改成10後,test.c中的a依然爲0。輸出結果:
clipboard.png

其實static還能夠用來修飾局部變量。

static和extern的總結

1.extern能夠用來聲明一個全局變量,可是不能用來定義變量

2.默認狀況下,一個全局變量是能夠供多個源文件共享的,也就說,多個源文件中同名的全局變量都表明着同一個變量

3.若是在定義全局變量的時候加上static關鍵字,此時static的做用在於限制該全局變量的做用域,只能在定義該全局變量的文件中才能使用,跟其餘源文件中的同名變量互不干擾


注:本文轉自李明傑老師的博文

相關文章
相關標籤/搜索