變數 (程式設計)

程式設計中,變數(英語:Variablescalar)是指一個抽象的儲存地址,它含有了被稱為一個的某種已知或未知的資訊,並且配對了關聯的符號名稱。通常使用變數名稱參照儲存值;將名稱和內容分開能讓被使用的名稱獨立於所表示的精確訊息之外。電腦原始碼中的識別字能在執行期間綁紮一個,且該變數的值可能在程式執行期間改變。 程式設計中的變數不一定能直接對應到數學中所謂的變數之概念。在程式設計中,變數的值不一定要為方程數學公式之一部分。程式設計中的變數可使用在一段可重復的程式:在一處賦值,然後使用於另一處,接着在一次賦值,且以相同方式再使用一次(見迭代)。程式設計中的變數通常會給定一個較長的名稱,以描述其用途;數學中的變數通常較為簡潔,只給定一、兩個字母,以方便抄寫及操作。

一個變數的儲存地址可以被不同的識別字所參照,這種情況稱之為別名。使用其中一個識別字為變數賦值,將會改變透過另一個識別字存取的值。

編譯器必須將代表變數的名稱替代成該數據所在的實際地址。變數的名稱、類型及地址通常會維持固定,但該地址所儲存之數據於程式執行期間則可能會改變。

用識別碼參照變數

用識別碼參照變數能對變數進行訪問,從而讀取變數的值,改變變數的值,或者改變變數的屬性(如訪問權限、狀態鎖定等)。

例如,一個變數用識別碼total_count來參照,設定這個變數的值為1981。如果該變數同時也用識別碼x 來參照,通過x將變數值改變為2010,那麼讀取total_count的值就是2010而不是1981。

如果某種程式語言只允許同一個變數用一個識別碼參照,那麼「該變數的名字」就是有意義的,否則我們稱之為「該變數的名字之一」。例如,在前面的那個例子當中,total_count是這個變數的名字之一,而x是這個變數的另外一個名字。

對變數的操作

指令式程式語言中,變數的值通常能夠隨時訪問或重新賦值。但在邏輯編程語言中,根據參數透明的需求,變數被繫結到表達式並且在它的整個生命周期中保持同一個值。在指令式程式語言中,同樣的行為用常數來表達,它和通常的變數存在反差。

根據程式語言的型別系統的不同,變數可能只儲存一種特定的資料類型(如整型字串型)。而另外一種情況,變數的資料類型能根據當前賦值而改變,允許單個變數儲存該程式語言支援的任何資料類型。

命名規範

數學當中的量不同,程式設計所用的變數和常數通常都採用多字元的名字,如count或者size。而單個字元的名字一般僅用於輔助性的變數,如ijk常作為陣列索引的變數。

一些命名規範是作為語法在語言層面強制執行的。在大多數語言當中,變數名不能以數字開頭,不能包含空格符。而標點符號是否允許存在在變數名當中就要視具體語言而定了。很多語言僅僅允許底線_存在在變數名當中,而禁止其他所有的標點符號;而有些程式語言,特殊字元作為字首或字尾添加在變數識別碼當中來表明變數的類型。變數名的大小寫敏感性也要視具體語言而定。大多數現代語言是大小寫敏感的,一些較老的語言則不敏感。一些語言保留特定形式的變數名用來內部使用,在很多語言中,以兩根底線開頭__的變數名常充當這種角色。

在語言語法基本的限制以外,進一步的命名風格規範也很有必要。在機械碼層面,是不會使用變數名的,所以電腦並不關心是否採用了準確的名字。正因為如此,變數名完全是作為程式設計師的工具而存在,藉助這個工具程式設計師能更容易的編寫和理解程式。程式設計師通常建立編碼規範,並且堅持這些規範,幫助對變數命名甚至提供精確的命名規劃。較短的名字便於輸入,但是描述能力較差;較長的名字使程式更容易讀懂,變數的意圖更容易理解。儘管如此,冗長的變數名也可能會導致更難理解的代碼。

在原始碼中

原始碼中,變數名是將變數和主記憶體地址繫結的一種方式。變數值以數據對象的形式儲存在相應的地址內,這樣該數據對象就能通過變數的名字進行訪問和修改了。

在電子試算表中

電子試算表中,一個儲存格可能包含參考其他儲存格的公式。這種被參考的儲存格就是一種形式的變數,它的值就是被參考的儲存格的值。

作用域和生存周期

變數的作用域表示變數在原程式的文字中能被使用的範圍。變數的生存周期表示變數在程式執行過程中具有實際意義的值的時間範圍。更通俗地說,變數的作用域是變數名字的性質,而變數的生存周期是變數本身的性質。

變數名字的作用域會影響它的生存周期。

作用域是變數語法方面的性質。多數語言對每一個變數(和其他名目實體)定義明確的作用域,這些作用域在同一個程式中可能不同。變數的作用域是指程式中的特定區域,在這些區域中,該變數的名字是有意義的並且變數是「可見的」。在進入作用域時,變數通常開始它的生命周期;而在離開作用域時,變數往往結束了它的生命周期。例如,某個變數的語法作用域僅在特定的陳述式塊或者子程式中。只有在某個函數中能訪問的變數則被稱為局部變數,在程式的任何一個地方都能參照的變數被稱為全域變數

生存周期,則是變數在執行時的性質。在執行時,每次變數與值的繫結都具有自己的生存周期。繫結的生存周期是程式執行過程中的一段時間,在這段時間內,變數始終被關聯到相同的值或者主記憶體位置。在閉包的情況中,執行中的程式可能進入和離開某個生存周期很多次。

在一些代碼段中,在一個變數的作用域中可能未被賦值,或者它的值已經被銷毀掉了。這類別變數常被稱為「生存周期外」或者「未繫結」。在很多語言中,試圖使用未繫結的變數是一個錯誤。在其他語言中,這種行為會產生不可預期的結果,這樣的變數可能被分配一個新的值。與之對照的是,一個變數繫結到一個超過他作用域的生存周期是被允許的,如Lisp的閉包和C語言的靜態局部變數。當程式再次執行到變數的作用域時,變數能再次被使用,但還保持上一次的值。

為了提高空間效率,變數需要的儲存空間可能要等到變數第一次使用時才申請,不再使用後就刪除。為了避免浪費空間,如果變數聲明了但不實際使用,編譯器通常會向程式設計師發出警告。

使變數的作用域儘可能的小,被認為是一個好的編程方式,這樣程式的不同部分就不會因為意外的改變對方的變數而互相影響了。實現上述目標的通常技術是讓程式的不同部分使用不同名字空間,或者通過動態變數作用使用各自的私有變數

很多程式語言使用保留的值(如NULL)表示沒有初始化的變數。

類型

靜態型別語言中,如JavaML,每個都變數有一個類型,也就是說只有給定種類的值能儲存到該變數中。一個基本類型的變數只能儲存基本類型的值。一個類類型的變數能儲存空值NULL,或者儲存該類型或其子類型的對象。一個介面類型的變數能儲存空值NULL,或者該介面的任何一個實現。一個陣列類型能儲存空值NULL或者一個陣列。

動態型別語言中,如Python,是值,而不是變數來攜帶類型資訊。在Common Lisp中,這兩種情況同時存在:變數在編譯時具有一個類型(如果沒有聲明,就假設這個類型為超類型T);值也有具有一個類型,該類型可以在執行時進行檢查和辨識。

變數的類型也允許在編譯時多型決定。但是,這和物件導向的函數呼叫(在C++中稱為虛擬函式)的多型不同。

變數常常儲存簡單的數據,如整數字串。但有些程式語言允許變數同時表示多種資料類型。這些語言一般也允許函數參數多型,其函數對變數的操作可同時適用於多種資料類型。例如,函數length可以求一個列表的長度,如果length類型簽章中包含一個類型變數,就可以實現參數多型。這樣,求列表中的元素個數就與列表元素的類型無關了。

參數

函數的形式參數也被稱為變數。例如如下的C++代碼段:

int AddTwo(int x)
{
    return x + 2;
}

AddTwo(5);  // 结果为7

變數x是「形參」,因為當函數被呼叫時會被給定一個值。整數5是「實參」,它給x一個值。在多數語言中,函數參數具有局部的作用域。這裏的變數x只能在AddTwo函數中有效(儘管如此,其他函數也可以使用自己的變數x)。

主記憶體分配

變數的主記憶體空間分配和它們值的表示方法是多種多樣的,這種區別體現在語言之間,也體現在給定語言的內部使用上。很多語言都實現了局部變數的空間分配方式。局部變數儲存在呼叫堆疊上,其生存周期維持在單個函數中,函數返回時這些主記憶體會自動被回收。(更一般的講,變數的名字是和一些特定的連續主記憶體塊的地址繫結,對變數的操作其實是對相應的主記憶體塊進行操作。)對於巨大或者編譯時不知道大小的數據,更常用的方法是使用「參照」。 這時記錄是值的地址而不是值本身,它們是從一種被稱為「」的主記憶體池中分配的。

繫結的變數具有值,一個抽象的值。在程式執行時,變數的值用電腦主記憶體中儲存的一些數據對象來表示。程式,或者說執行時環境,必須為每個數據對象設置主記憶體。由於主記憶體是有限的,為了安置每一個數據對象,當數據對象不再表示某個變數的值時,相應的主記憶體會被回收並重新使用。

在堆中分配的對象必須被釋放掉,特別是當對象不再被需要時。在具有垃圾回收機制的語言(如C#JavaLisp)中,當變數出了其作用域再也不能被參照時執行環境會自動地回收對象。在不具有垃圾回收機制的語言當中,如C語言,程式必須顯式地分配主記憶體,而且使用結束後還要釋放主記憶體,如果不這樣做則會造成記憶體流失。在這種情況下,程式執行過程中會逐漸消耗,最終因為主記憶體耗盡而崩潰。

當一個變數指向動態建立的數據結構時,可能其中一些部分只能通過變數間接地訪問。在這種環境下,垃圾回收器(或者類似的語言特性)必須處理當變數回收時只有一部分主記憶體能夠重新回收的情況。

名字替換