一、 鏈接屬性
C語言中鏈接屬性決定如何處理在不同文件中出現的標示符。標示符的作用域與它的鏈接屬性有關,但這兩個屬性并不相同。
鏈接屬性有3種: external(外部),internal(內部) 和 none(無)。
1. none: 沒有鏈接的標示符,總是被當做單獨的個體,也就是說改標示符的多個聲明被當做不同的實體。
2. internal: 在同一個源文件內的所有聲明中都指同一個實體,但位于不同源文件的多個聲明則分屬不同的實體。
3. external: 標示符不論聲明多少次,位于幾個文件都表示同一個實體。
舉一個簡單的例子對鏈接屬性進行說明,如下圖:
1. 在缺省情況下,標示符b,c,f 的鏈接屬性為external, 其余標示符的鏈接屬性則為none。因此,另一個源文件也包含了標示符b的類似聲明并調用函數c,他們實際上訪問的是這個源文件所定義的實體。f的鏈接屬性之所以是external,是因為它是函數名。這個源文件中調用f,實際上將鏈接到其他源文件所定義的函數,甚至這個函數的定義可能出現在某個函數庫。
2. 關鍵字extern和static用于在聲明中修改標示符的連接屬性。如果某個聲明在正常情況下具有external鏈接屬性,在它前面加上static關鍵字可以使它的鏈接屬性變為internal。例如,將b聲明為
static?int?b;那么變量b就將為這個源文件所私有。在其他源文件中,如果也鏈接到一個叫做b的變量,那么它所引用的是另一個不同的變量。
3. static只對缺省鏈接屬性為external的聲明才有改變鏈接屬性的效果。例如,你盡管可以在變量e前面加上static關鍵字,但它的效果完全不一樣,因為e的缺省鏈接屬性不是external。
二、 存儲類型
變量的存儲類型是指存儲變量值的內存類型。有三個地方可以用于存儲變量:普通內存、運行時堆棧、硬件寄存器。變量的缺省存儲類型取決于它的聲明位置。
1. 凡是在任何代碼塊之外聲明的變量總是存儲于靜態內存中,也就是不屬于堆棧的內存,這類變量稱為靜態(static)變量。靜態變量在程序運行之前創建,更確切的說,是在將可執行文件加載到內存的時候創建,其在程序的整個執行期間始終存在。
2. 在代碼塊內部聲明的變量的缺省存儲類型是自動的(automatic), 也就是說它存儲于堆棧中,稱為自動變量。有一個關鍵字auto就是用于修飾這種存儲類型的,但它極少使用,因為代碼塊中的變量缺省情況下就是自動變量。在程序執行到聲明自動變量的代碼塊時,自動變量才被創建,當程序的執行流離開代碼塊時,這些自動變量便自行銷毀。在代碼塊內部聲明的變量,如果給它加上static,可以使它的存儲類型從自動變為靜態。注意,修改變量的存儲類型并不表示修改改變量的作用域。它任然只能在該代碼塊內部按名字訪問。函數的形式參數不能聲明為靜態,因為實參總是在堆棧中傳遞給函數。
3. 關鍵字register可以用于自動變量的聲明,提示它們應該存儲于機器的硬件寄存器而不是內存中。
三、 static關鍵字使用說明
注意到在“連接屬性”和“存儲類型”中都有可能使用到static關鍵字,因為我們有必要搞清楚在不同情況下,static關鍵字的作用。
1. 當它作用于函數定義時,或者用于代碼塊之外的變量聲明時,static關鍵字用于修改標示符的鏈接屬性。
從external改為internal,但標示符的存儲類型和作用域不受影響。用這種方式聲明的函數或變量只能在聲明它們的源文件中訪問。
2. 當它作用于代碼塊內部的變量聲明時,static用于修改變量的存儲類型。
從自動變量修改為靜態變量,但變量的鏈接屬性和作用域不受影響。用這種方式聲明的變量在程序執行之前創建,并在程序的整個執行期間一直存在。
四、 作用域、存儲類型示例
我們就以下面的示例代碼進行說明。
1. 第1行a的鏈接屬性為external,第二行extern在技術上并非必要,第三行的static關鍵字修改了c的缺省鏈接屬性,把它改為internal。聲明了變量a和b(具有external鏈接屬性)的其他源文件在使用這兩個變量時實際所訪問的是聲明與此處的這兩個變量。但變量c只能由這個源文件訪問,因為它具有internal鏈接屬性。
2. 變量a,b,c 的存儲類型為靜態,表示它們并不存儲于堆棧中。因此,這些變量在程序執行之前創建,并一直保持它們的值,直到程序結束。
3. 第4行聲明了兩個標示符,d的作用域從第四行直到文件結束。對于函數而言,存儲類型不是問題,因為代碼總是存儲在靜態內存中的。參數e不具有鏈接屬性,所以我們只能從函數內部通過名字訪問它。
4. 第6~8行聲明局部變量,所以它們的作用域到函數結束為止,它們不具有鏈接屬性,所以它們不能在函數的外部通過名字訪問。變量g的存儲類型是靜態,所以它在程序的整個執行過程中一直存在。當程序開始執行時,它被初始化為20。當函數每次被調用時,它并不會被重新初始化。
5. 第9行的聲明并不需要,這個代碼位于第1行聲明的作用域之內。
6. 第12,13行代碼塊聲明為局部變量。它們都具有自動存儲類型,不具有鏈接屬性。
7. 第14行使全局變量h在這個代碼塊內可以被訪問。它具有external鏈接屬性,存儲于靜態內存中。
8. 第19,20行用于創建局部變量。
9. 第25行聲明了函數i,它具有靜態鏈接屬性。
五、 作用域、鏈接屬性和存儲類型總結
六、 變量存儲區域分配圖
大家可以根據上述示例代碼,將變量一一對應的放到指定的區域。