C語言筆記之儲存類
在C語言中,一個數據物件(往往是指變數)可以由3種屬性來描述:作用域,儲存時期,連結。每種屬性有不同的值,這些不同值的組合構成資料物件的儲存模型,又稱為儲存類,即,一個數據物件如何存在於計算機中。以下描述中,我們以變數為例(另一個例子是函式)代表資料物件。
一、作用域
作用域屬性描述了一個數據物件可以在(原始碼的)哪些區域被訪問,它有以下幾個值:程式碼塊、函式原型、檔案。
1、檔案作用域
函式是C原始碼檔案的基本組織單位,但是如果一個變數定義在所有函式之外,即沒有在任何一個函式體內,那麼該變數就具有檔案作用域,其範圍是從變數定義處直到檔案結尾。在此範圍內的任何一個地方都可以訪問它。需要注意的是,C語言規定所有變數都必須先宣告再使用,所有在定義變數之前的地方是不能訪問該變數的。
2、函式原型作用域
首先應弄清楚什麼是函式原型。有關函式的幾個概念很容易混淆,它們是:函式原型、函式定義、函式宣告。函式宣告包括前兩者,而函式原型和函式定義的區別在於有沒有函式體,有就稱為定義,沒有就是原型。更本質的區別在於,函式定義是要分配儲存空間的,而函式原型不必。
函式原型的作用域是從變數定義處直到原型宣告的末尾,但是這個作用域似乎沒啥用啊。。。
3、程式碼塊作用域
程式碼塊是指包含在一對花括號之間的區域,程式碼塊作用域的範圍也就是這對花括號之間。
void show(int i) {
int j;
if (i < 0) {
int k = 2;
i = i + k;
}
j = i * 3;
return j;
}
在這段程式碼中,k定義在if{}塊中,所以在該塊之外k是無法訪問的,比如把j = i * 3;換成j = i * k;就是錯的。
要注意的是,這段程式碼是一個函式定義,在定義中有形式引數i,它的作用域也是程式碼塊,即show{}的函式體內,而不是函式原型中的那樣。也許這就是為什麼把函式原型作用域單獨提出來的原因吧。。。
C99把程式碼塊的概念擴大到for() while() if()語句的圓括號,在圓括號內定義的變數也具有程式碼塊作用域。
二、儲存時期
該屬性描述了一個變數的“存活時間”,它有兩個值:靜態儲存時期和自動儲存時期。前者在程式執行期間一直存在;後者在使用完畢之後就被系統自動清除,釋放記憶體。
儲存時期和作用域是相互關聯的,不能隨意組合。一般來說,具有檔案作用域的變數都有靜態儲存時期;而程式碼塊作用域變數具有自動儲存時期。
三、連結
連結屬性描述了一個變數是否可以被其他檔案引用。一個C程式往往由多個C原始檔組成,所以在一個檔案中定義的變數如果可以在另一個檔案中被引用會方便很多。但是也有一些變數是專門為本檔案服務的,不希望被其他檔案引用,否則會引起混亂。連結屬性就是為解決這一問題設計的。
該屬性有3個值:內部連結、外部連結和空連結。連結同樣和作用域緊密相關,不能隨意組合。一般來講,具有檔案作用域的變數可能具有內部連結或外部連結,而具有程式碼塊作用域或函式原型作用域的變數只有空連結。具有外部連線的變數可以被其他檔案使用;具有內部連結的變數只能在本檔案內部使用;而具有空連結的變數只能在程式碼塊或函式原型內使用。
四、組合
三種屬性經過“有條件的”組合之後形成以下五種儲存類:自動、暫存器、具有外鏈的靜態、具有內鏈的靜態、具有空鏈的靜態。用下圖說明它們的各個屬性組合:
5種儲存類按照作用域來劃分為程式碼塊和檔案兩大類,作用域確定了,其他的都好說。
儲存時期的自動與靜態可以分別用關鍵字auto和static來顯式宣告,而連結的內部或外部分別由static和extern來修飾。但是對於不同作用域的變數,存在著預設情況。
由上表可知,只要是作用域為程式碼塊的變數,其儲存時期預設為自動,從而auto都是省略不寫的;但是要使這樣的變數具有靜態儲存時期,就必須顯式的使用static宣告。連結型別的話,都是空連結,不再區別內外。也可以看到,這類變數都是定義在程式碼塊內部的,拋開暫存器變數這個異類,這類變數之間的唯一區別就在於有沒有被static修飾(從而意味著是不是靜態變數)。
另一大類變數就是定義在所有函式之外的具有檔案作用域的變量了,它們的儲存時期只能是靜態的,不可以被auto修飾;而連結型別預設為外鏈,於是extern一般都省略不寫。要使之成為內鏈,則需使用static修飾。
可以總結一下static的作用:顯然它對不同作用域的變數的作用是不同的,對程式碼塊作用域的變數,它的作用是使之成為靜態變數;而對於檔案作用域的變數,它的作用是使之成為內鏈變數。
把變數劃分這麼細有啥用呢?比如,在檔案開頭定義了一個變數a,然後在函式內部又定義了一個同名變數,那麼計算機會不會混淆呢?在函式內部到底誰在起作用呢?這時,儲存類就派上用場了:不同的儲存類變數會被存放在記憶體的不同區域,即使它們是重名的。在函式內部,內部定義的變數將覆蓋外部變數,起作用的將是內部變數。
還有,就是兩類變數的初始化方案不同:檔案作用域變數如果沒有初始化,系統會自動為其賦一個預設值,但是程式碼塊作用域的變數享受不到這個待遇。