轉:左值 右值

來源:互聯網
上載者:User

轉自:http://blog.csdn.net/slowgrace/archive/2009/10/20/4704066.aspx

Q:作為一個程式員,為什麼要弄明白左值的概念?
A:有很多原因。比如說,有些語境下必須要使用左值,如果你不知道哪些運算式是左值,你就可能給錯。

 

Q:請問哪些語境下必須要使用左值?
A:下列運算子的運算元要求左值:sizeof運算子, 取地址運算子 & , ++ 運算子, -- 運算子,賦值 = 運算子的左側,成員 . 運算子的左側。

Q:那麼如何判斷一個運算式是左值?
A:依據標準的定義來判斷。[C99]An lvalue is an expression with an object type or an incomplete type other than void; 也就是說,如果一個運算式具有物件類型或非空不完整類

型,那就是左值。其實這裡關鍵的是物件類型,雖然不完整類型不是物件類型,但由於它可以通過某種方式補充完整,因此可以認為它也是一種物件類型;但void除外,因為void不能被補充

完整。

Q:那麼如何判斷一個運算式具有物件類型?
A:那我們要先搞清楚什麼是物件類型。Types are partitioned into object types (types that fully describe objects), function types (types that describe functions), and

incomplete types (types that describe objects but lack information needed to determine their sizes). 所以“通過非函數型別宣告的非類型標識符”都是左值。

Q:那麼如何判斷那些由標識符和運算子共同構成的運算式是否左值呢?
A:每種運算子都規定了它的運算結果是否左值以及它的運算元是否期待左值,依據這些規則就可以判定一個運算式是否左值(以下簡稱它們是左值規則)。其實也不用記得太牢,因為如果你

弄錯了,編譯器會報錯的,嘿嘿。

Q:有哪些常見的左值規則?
A:
(1)最明顯的左值運算式的例子是有適當類型和記憶體的標識符;
(2)間接運算子*的運算結果是左值;
(3)取地址運算子&的運算結果是右值;
(4)下列運算式不能產生lvalue: 數組名,函數,枚舉常量,賦值運算式,強制類型轉換(目標類型是引用時除外),函數調用。

Q:暈,*ptr和&a都是指標,為嘛一個是左值、一個是右值啊?
A:簡單說,這就是規則。如果你覺得死記硬背這個規則比較累的話,不妨看看飛雪關於*ptr返回對象的解釋,來協助你的記憶:*在這裡是解引用運算,相當於說,有一個對象被盒子包起來

了,然後用*運算開啟盒子,使用這個對象。也就是ptr這個指標包裹了它所指向的對象pa,通過*運算,就擷取了pa這個對象。

Q:明白一點了,有沒有什麼更通用的協助理解記憶上述規則的方法呢?
A:來看hpsmouse寫的這個心得:左值和右值的概念寫代碼寫多了就會自然產生相應感覺的——就是飛雪提到的“可訪問的儲存”。一些感性的例子:
  a+b這個值是一個相當“懸”的東西,我們只知道它是 a+b 的結果,但不能掌握它的具體情況,也不太關心它到底怎樣出現甚至是否出現。
  而 *p 就不一樣,因為 p 是個指標,它必然有一個值,不管是有效還是無效的,那麼那個值代表的記憶體地區就肯定有某個東西,這是實實在在的。
  至於 &a,我們只知道它是 a 的地址,同樣不知道也不關心它到底怎樣出現或是否出現。
於是,我們把 a+b、&a 這樣有些“虛”的運算式稱為 rvalue,把 *p 這樣實實在在的運算式稱為 lvalue。

其實,hpsmouse“把 *p 這樣實實在在的運算式稱為 lvalue”,多少觸及到了lvalue的重要實質(pmerOFc語)。實際上,lvalue中的“l”可以理解為“location” (來自這篇文章,謝謝

Tiger_Zhao提供連結)。早期的左值定義(比如C89)指的就是一個其結果有adrressable location(可以定址的儲存)的運算式,你可以往這個location放資料或資訊。(The "l" in

lvalue can be though of as location, meaning that an expression has an addressable location that data or information can be put into.)

Q:貌似有點明白了,為什麼要強調可訪問的儲存?難道還有些擁有儲存卻不可訪問的運算式結果嗎?
A:對的,來看如下例子(來自飛雪):
int a;
int foo(){return 1;}
foo 的返回的是一個int,這個int的值是1,這個值是擁有儲存的,但是你不應該知道;a + 1會通過+產生一個值,這個值是擁有儲存的,但是這個儲存也不是你應該知道的。所以,它們都不

是左值。只有當你擁有運算式的儲存的訪問權時,你才可以把這個運算式放在=的左邊,通過賦值來改變這個對象的狀態。

其實左值無非是可以引用到對象的運算式,因此左值的概念和C裡的對象是密不可分的,只要理解好了對象,就比較好把握左值了。C裡的對象 (注意和“物件導向”裡的“對象”完全兩回事

)是指一塊命名的記憶體地區(An Object is a named region of storage—From “The C Programming Language”)。所以,左值的關鍵是擁有你可訪問的儲存!

當然左值概念經過發展後,已經不再介意一個左值引用的對象是否真的存在了,重要的是,這個左值具有對象或非空不完整類型。例如(來自supermegaboy):
double i;
int *p = ( int* )&i; /*p並非真正指向一個int對象,但*p依然是左值*/
int *p;
*p = .........; /*p並沒有指向啥,但*p還是左值*/

Q:說了這麼多左值,那右值的定義是什麼呢?
A:[C99]右值(rvalue)是指運算式的值。(46頁尾注)What is sometimes called “rvalue” is in this International Standard described as the “value of an expression”. 實

際上,右值裡的“r”其實也有“read”的意思(The "r" in rvalue can be thought of as "read" value—來自這篇文章),也就是要把存在左值的儲存空間裡的東西讀出來。當然,這隻

是個用於協助理解記憶的經不起推敲的說法,實際中很多右值並沒有對應的左值,更談不上從什麼地方讀出來了。

Q:有點暈。那左值運算式的值也是右值?
A:恩,對。實際上,除了上面必須使用左值的場合以外,所有左值運算式(數群組類型的左值除外)在使用的時候其實是被轉化為它所引用的對象所儲存的值使用的。 Except when it is the

operand of the sizeof .operator. the unary h operator. the ++ operator. the -- operator. or the left operand of the . operator or an assignment -operator. an lvalue

that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue). (上述道理和pmerOFc爭論了大半夜才弄明白,感謝

pmerOFc和supermegaboy。以上內容譯摘自C89 6.2.2.1)。

也就是說,在C中,一個左值運算式,除了應用於一些特定的運算子時以外,在使用時會自動進行左值到右值的轉換,並一直進行到無法再轉換為止。因此,在C裡運算式的值一定是右值

(supermegaboy 語),而且一定是不擁有可訪問的儲存的。在C++標準裡也有類似的說法(飛雪提供)Whenever an lvalue appears in a context where an rvalue is expected, the

lvalue is converted to an rvalue。

小結:
(1)定義和含義
  a)左值是指具有物件類型或非空不完整類型的運算式。(關鍵是要可以引用到對象,也就是要可以擁有可訪問的儲存,l-location)
  b)右值(rvalue)是指運算式的值。(在C裡運算式的值一定是右值;在期待右值時,左值會自動轉化為右值。r-read)
(2)依據下述規則來判斷左值:
  a)“通過非函數型別宣告的非類型標識符”都是左值
  b)每種運算子都規定了它的運算結果是否左值。
(3)常見規則
  a)下列運算子的運算元要求左值:Sizeof運算子, 取地址運算子 & , ++ 運算子, -- 運算子,賦值 = 運算子的左側,成員 . 運算子的左側。
  b)間接運算子*的運算結果是左值;取地址運算子&的運算結果是右值。
  c)下列運算式不能產生lvalue: 數組名,函數,枚舉常量,賦值運算式,強制類型轉換(目標類型是引用時除外),函數調用。

小結的小結:要確認一個運算式是否左值,總的來說要按規則來。要理解記憶這些規則,可以按“擁有可訪問的儲存“來。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.