值 (電腦科學)

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

電腦科學中,(英語: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.

外部連結

編輯