值 (電腦科學)

表达式在计算机科学无法进一步评估

計算機科學中,(英語:Value)是一無法進一步求值表達式[1]例如,表達式「1 + 2」不是一個值,因為它可以被化簡為表達式「3」。表達式「3」不能夠繼續化簡,因此它是一個值。表達式既有類型(type)屬性,又有值分類(value categories)屬性。兩種屬性彼此獨立。[2]也就是說,對每一種類型的表達式,都有各種值分類。

大多數編程語言支持幾種常見的值。

賦值:左值和右值

一些語言使用左值l-value)和右值r-value)的概念。左值具有確定的、可以被獲得的內存地址。這意味着左值可以是變量,也可以是對指向特定內存地址的指針解引用(dereference)的結果。例如C語言的表達式(4 + 9),在執行時,計算機生成一個整數值13,但因為程序沒有明確指定這個13如何在計算機中存儲,所以這個表達式產生一個右值。另一方面,如果一個C程序聲明了一個變量x並將x賦值為13,那麼表達式(x)的值是13,並且是一個左值。

在C語言中,術語「左值」最初表示可以被賦值(即位於賦值運算符左側)的對象,但由於「const」被加入到語言中,這類對象現在被稱作「可更改的左值」。C++中,左值和右值是表達式的分類,一個表達式必然是左值或右值之一。C++11把上述左值性(lvalueness)擴充為更複雜的值類別(value category),包含左值(lvalue),純右值(prvalue)和臨終值(xvalue)三個基本分類(fundamental classification),一個表達式必然是三者之一。

左值和右值的概念最早由CPL程序語言的一篇論文引入。[3] 這時定義的左值是可以放在賦值號左側賦予新值的對象;[4]右值是可以放在賦值號右側讀出其值的對象。在B語言中,左值和右值作為文法的元素被明確。在C語言中,文法不再出現左值和右值的區別,左值的概念被保留在語義規則中,而「右值」在ISO C中被視為「值」的同義詞。

C++語言引入了const限定的對象。const對象可以取地址,但是不能被賦值;而一些右值對象也可以出現在賦值號的左邊被賦值。[5]因此,截至C++03標準,把具有標識(identity)的表達式規定為左值,不具有標識的表達式規定為右值。因而,名字、指針、引用等是左值,是命名對象,具有確定的內存地址;字面量、臨時對象等為右值,右值僅在創建它的表達式中可以被訪問。函數名字是左值(在C語言中規定它既不是左值也不是右值),數組名是常量左值,但是在大多數表達式中函數名字與數組名字自動隱式轉換為右值。右值的生存期短暫,所以需要用左值去捕捉右值。把右值複製(copy)到左值上是常見操作。

C++11標準引入了右值引用數據類型與移動語義,因而左值與右值的定義發生了很大變化。右值引用變量綁定到右值上,延長了右值對應的臨時對象的生存期。移動語義把臨時對象的內容移動(move)到左值對象上。因而在C++11,對於值的分類,要考慮標識(identity)與可移動性(movability),二者的組合產生了五種分類:

  • 基礎值類型
    • 左值lvalue:可以用取地址運算符&獲取地址的表達式。也可定義為非臨時對象或非成員函數。具有標識,但不可移動。這也是C++03的經典左值。可用於初始化左值引用。可以有不完備類型(incomplete type)。包括:
      • 作用域中的變量名與函數名,不論其類型。因此,具名的右值引用,即具有右值引用類型的變量,也是左值表達式,這符合一般規律,不是特例。
      • 函數調用表達式或重載運算符表達式,如果其返回類型為左值引用或者是到函數類型的右值引用。 [6]
      • 內建的先增(前綴++)、先減(前綴--)、解引用(dereference)、賦值、複合賦值、下標(除了數組臨終值)、成員訪問(除了臨終值的非靜態非引用成員、成員枚舉值、非靜態成員函數),通過數據成員指針的訪問且左端操作數為左值、逗號運算符且右端的操作數為左值、三元條件運算符(ternary conditional)且第二與第三操作數為左值。
      • 到左值引用類型的類型轉換表達式。
      • 字符串字面量(string literal)
      • 類型轉換表達式,轉換為到函數的右值引用
    • 臨終值xvalue(expiring value):具有標識,並且可以移動。對應的對象接近生存期結束,但其內容尚未被移走。可以多態;非類對象可以cv限定。包括:
      • 函數調用或重載的運算符表達式,如果返回類型是到對象的右值引用[6]
      • 類型轉換表達式,轉換為右值引用,如static_cast<T&&>(val)或(T&&)val
      • 訪問xvalue的非靜態類成員。
      • 指向數據成員的指針表達式,第一操作數是xvalue
    • 純右值prvalue:不具有標識,但可以移動。對應臨時對象或不對應任何對象的值。純右值不能是多態的;臨時對象的動態類型是表達式類型;非類且非數組的純右值不能是const限定的;不能有不完備類型(除了void)。包括:
      • 字面量(除了字符串字面量)。
      • 函數調用或重載的運算符表達式,如果返回類型不是引用。[6]
      • 內建後增、後減、算術與邏輯運算符、比較運算符、取地址運算符、訪問成員枚舉值、訪問非靜態成員函數、訪問右值的非靜態非引用數據成員、訪問右值的數據成員指針或非靜態函數成員指針、逗號運算符且右端操作數為右值、三元條件運算符且第二或第三操作數不是左值。
      • 類型轉換表達式,轉換為非引用類型。
      • Lambda表達式
  • 廣義左值glvalue:具有標識。包括左值與臨終值。可以多態、動態類型。
  • 右值rvalue:可以移動。包括瀕死值與純右值。不能通過&運算符取地址。

C++的非靜態成員函數調用表達式(obj.func與ptr->func),非靜態成員函數指針調用表達式(obj.*mfp與ptr->*mfp)被當作純右值,但是不能用於初始化引用,不能做函數實參,僅僅能用作函數調用表達式左邊的操作數,如(pobj->*ptr)(args)。

返回void的函數調用表達式、到void的類型轉換表達式、throw表達式被當作純右值。但是不能用於初始化引用,不能做函數實參。可用於某些上下文環境中(如單獨作為一行語句、逗號操作符的左端表達式等),或返回void的函數的return語句中。此外,throw表達式可用作三元條件操作符的第二或第三操作數。

位元欄(bit field)表達式是左值,但不能用&運算符取地址,不能綁定到非常量左值引用。常量左值引用可以用位域左值初始化,但實際上是另行分配綁定了一個對象。

匯編語言

值可以是一個給定的數據類型,例如一個字符串,一個數字,一個單一的字母等幾乎任何類型的數據。

有些處理器支持多種尺寸的立即數,例如8位或16位,每一種指令形式採用獨特的操作碼和助記符。如果一個程序員提供的數據值不適合,匯編器將會出現「超出範圍」的錯誤消息。大多數匯編器允許一個立即數被表示為ASCII十進制十六進制八進制二進制數據。因此,ASCII字符'A'和65、0x41是一樣的。字符串的字節序在不同處理器之間可能不同,取決於匯編器和計算機體系結構。

參考資料

  1. ^ Mitchell 1996,第92頁.
  2. ^ "Value categories",in cppreference.com. [2014-07-27]. (原始內容存檔於2020-11-08). 
  3. ^ [D.W., Barron, J.N., Buxton, D.F., Hartley, E. Nixon, C. Strachey: The main features of CPL, published in The Computer Journal (1963) 6 (2): 134-143. fulltext: http://comjnl.oxfordjournals.org/content/6/2/134.full.pdf+html頁面存檔備份,存於網際網路檔案館)]
  4. ^ 「Anything that can appear on the left-hand side of an assignment expression is an lvalue.」
  5. ^ 例如,右值對象的數據類型重定義了賦值運算符。
  6. ^ 6.0 6.1 6.2 《C++語言標準》5.2.2.10:A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.

外部連結