概念與定義
“靜態變數”這一術語有兩個容易混淆的定義:
語言無關的通用定義:與程式有著相同
生命周期的變數;
C族語言特有的定義:以static存儲類聲明的變數。
而在以
Pascal為代表的許多
程式語言中,所有
局部變數都由系統自動分配存儲空間,而所有
全局變數的存儲空間則以靜態分配的方式獲取(對應“靜態變數”),因此由於實際上“局部變數”和“全局變數”這兩個
術語已足以涵蓋所有的情況,在這些程式語言中通常不使用“靜態變數”這一術語,而直接以“全局變數”代之。一般來說,在這些程式語言中,靜態變數就是全局變數,而即使在有明確區分全局和靜態變數的程式語言中,在
編譯後的代碼里二者也以相同的方式獲取存儲空間。而今術語“靜態變數”的概念則主要基於C族語言的“static”的定義(即定義2)。
作常量使用
靜態變數也可以用於存儲
常數。具體來說,靜態變數(全局變數及
彙編語言里定義的符號亦同)可用const,constant或final(根據語言決定)等關鍵字標識,這時其值就會在編譯時設定,並且無法在
運行時改變。編譯器通常將靜態常量與文本一起置於
目標檔案的文本區域,而非常量
初始化數據則置於數據區;而如若有需要,有些
編譯器還可選擇為其開闢專用區;為防止常數變數被錯誤的
指針寫入覆蓋,亦可在這塊區域啟用記憶體保護機制。
C族語言中的實現
在
C語言及由其派生出的
C++與
Objective-C等程式語言中,“static”是用於控制變數的生命周期和
連線方式(即其
作用域,亦即可見性)的
保留字。確切來說,正如C族語言中的extern,auto與register這些保留字一樣,static也是一種存儲類(此處的“類”與面向對象語言的“
類”的定義不同)標識。每個變數與函式都有以上的一種存儲類標識,如果在
聲明中沒有明確標識其存儲類,編譯時就會根據上下文來選擇其默認存儲類,如在源檔案里的所有檔案級變數對應的默認存儲類是extern,而在函式體內的變數對應的則是auto,各存儲類的屬性如下表所列。
存儲類名 | 生命周期 | 作用域 |
---|
extern | 靜態(程式結束後釋放) | 外部(整個程式) |
static | 靜態(程式結束後釋放) | 內部(僅翻譯單元,一般指單個源檔案) |
auto,register | 函式調用(調用結束後釋放) | 無 |
易見存儲類為extern的變數(包括上面提到的未明確聲明存儲類的檔案級變數)匹配前段所述靜態變數的定義1,但不匹配定義2。
不同情況下的作用
除明確標識出變數的
生命周期外,將變數聲明為static存儲類還會根據變數屬性不同而有一些特殊的作用:
對於靜態
全局變數來說,針對某一源檔案的以static聲明的檔案級變數與
函式的作用域只限於檔案內(只在檔案內可見),也即“內部連線”,因而可以用來限定變數的
作用域;
對於靜態
局部變數來說,在函式內以static聲明的
變數雖然與自動局部變數的作用域相同(即作用域都只限於函式內),但存儲空間是以靜態分配而非默認的自動分配方式獲取的,因而存儲空間所在區域不同(一般來說,靜態分配時存儲空間於編譯時在程式
數據段分配,一次分配全程有效;而自動分配時存儲空間則是於
調用棧上分配,只在調用時分配與釋放),且兩次調用間變數值始終保持一致;必須注意,靜態局部變數只能初始化一次,這是由編譯器來保證實現。
對於靜態
成員變數來說,在C++中,在類的定義中以static聲明的成員變數屬於
類變數,也即在所有類實例中共享,與之相對的就是
過程變數。
C示例
在C語言中,帶有靜態變數的程式如下所示:
#include <stdio.h>void func() { static int x = 0; // 在對func的三次調用中,x只進行一次初始化 printf("%d\n", x); // 輸出x的值 x = x + 1;}int main(int argc, char * const argv[]) { func(); // 輸出0 func(); // 輸出1 func(); // 輸出2 return 0;}
C++示例
在C++中,帶有含私有靜態內部變數的類的程式如下所示:
class Request {private: static int count; // 不能為外部調用 string url; // 只能被成員函式調用public: Request() { count++; } string getUrl() const { return url; } void setUrl(string value) { url = value; } static int getCount() { return count; }};int Request::count = 0; // count 可以在類聲明外進行初始化