靜態變數
![本頁使用了標題或全文手工轉換](http://images.weserv.nl/?url=//upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Zh_conversion_icon_m.svg/35px-Zh_conversion_icon_m.svg.png)
靜態變數(英語:Static Variable)在電腦編程領域指在程式執行前系統就為之靜態分配(也即在執行時中不再改變分配情況)儲存空間的一類變數。與之相對應的是在執行時只暫時存在的自動變數(即局部變數)與以動態分配方式取得儲存空間的一些對象,其中自動變數的儲存空間在呼叫棧上分配與釋放。
概念與定義
「靜態變數」這一術語有兩個容易混淆的定義:
- 語言無關的通用定義:與程式有著相同生命周期的變數;
- 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聲明的變數雖然與自動局部變數的作用域相同(即作用域都只限於函式內),但儲存空間是以靜態分配而非預設的自動分配方式取得的,因而儲存空間所在區域不同(一般來說,靜態分配時儲存空間於編譯時在程式資料段分配,一次分配全程有效;而自動分配時儲存空間則是於呼叫棧上分配,只在呼叫時分配與釋放),且兩次呼叫間變數值始終保持一致;必須注意,靜態局部變數只能初始化一次,這是由編譯器來保證實現。[1]
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 可以在类声明外进行初始化
PHP範例
<?php
function test(){
static $a = 0;//变量$a在第一调用test()时被初始化,每次调用 test() 函数都会输出 $a 的值并加 1
echo $a;
$a++;//,每次调用 test() 函数都会输出 $a 的值并加 1
}
?>
參見
參考
- C程式設計語言(第二版),布萊恩·柯林漢與丹尼斯·里奇著 (Prentice Hall, 1984; ISBN 0-13-110362-8)
- C++程式設計語言(特別版),比雅尼·史特勞斯特魯普著(Addison Wesley, 2000; ISBN 0-201-70073-5)
- The GNU C Reference Manual (頁面存檔備份,存於網際網路檔案館)(英文), GNU.org
- Static Variables (頁面存檔備份,存於網際網路檔案館)(英文), University of Hawaii
- ^ 例如,gcc編譯器對靜態局部變數,首先取得guard變數,判斷低位元組是否為 0,若非零,表示已經初始化,可以直接使用。否則,將 guard 作為參數呼叫 __cxa_guard_acquire,如果鎖成功,執行初始化靜態變數的語句,然後釋放鎖。如果鎖失敗,說明產生競態條件,則會阻塞當前執行緒。利用該機制,可以很好的實現所謂 Singleton 模式。對於單執行緒程式,靜態變數初始化的互斥保護是沒有必要的,gcc的-fno-threadsafe-statics 選項可以禁掉該機制。
|