左值與右值

左值就是那些可以出如今賦值符號左邊的東西。
html

 右值就是那些能夠出現賦值符號右邊的東西。程序員

摻合彙編 聯想
  例如: 
       a = b + 25;
     a就是一個左值,由於它標識了一個能夠存儲結果值的地點,b + 25是個右值,由於它指定了一個值。可是它們能夠互換嗎? b + 25 = a; 原先用做左值的a此時也能夠看成右值,由於每一個位置都包含一個值。然而,b + 25不能做爲左值,由於它並未標識一個特定的位置。所以,這條賦值語句是非法的。
     注意當計算機計算b + 25時,它的結果必然保存於機器的某個地方。可是,程序員並無辦法預測該結果會存儲在什麼地方,也沒法保證這個表達式的值下次還會存儲於那個地方。其結果是,這個表達式不是一個左值。基於一樣的理由,字面值常量也都不是左值。
     聽上去彷佛是變量能夠做爲左值而表達式不能做爲左值,但這個推斷並不許確。在下面的賦值語句中,左值即是一個表達是式。
    int a[30];
    ...
    a[b + 10] = 0;
    下標引用其實是一個操做符,因此表達式的左邊其實是表達式,但它倒是一個合法的左值,由於它標識一個特定的位置,咱們之後能夠在程序中引用它。這裏另一個例子:
    int a, *pi;
    ...
    pi = &a;
    *pi = 20;
    第2條賦值語句,它左邊的那個值顯然是一個表達式,但它倒是一個合法的左值。爲何?指針pi的值是內存中某個特定位置的地址,*操做符使機器指向那個位置。當它做爲左值使用時,這個表達式指定須要進行修改的位置。當它做爲右值使用時,它就是提取當前存儲於這個位置的值。
    指針中的左右值舉例:
    char cp = 'a';
    char *cp = &ch;
    一、&ch 能夠做爲右值,可是不能做爲左值。爲何這個不是一個合法的左值?由於&操做符的結果是一個右值,它不能看成左值使用。當表達式&ch進 行求值時,它的結構應該存儲於計算機的什麼地方呢?它確定會位於某個地方,但你沒法知道它位於何處。這個表達式並未標識任何機器內存的特定位置,因此它不 是一個合法的左值。

   二、cp 能夠做爲左值,也能夠做爲右值

   三、&cp 能夠做爲右值,可是不能夠做爲左值。

   四、*cp 能夠做爲右值,也能夠做爲左值。

   五、*cp + 1 能夠做爲右值,可是不能夠做爲左值。*的優先級高於+,因此首先執行間接訪問操做,咱們能夠獲得它的值。咱們取得這個值的一份拷貝並把它與1相加,表達式 的最終結果爲字符'b'。這個表達式的最終結果的存儲位置並未清晰定義,因此它不是一個合法的左值。優先級表格證明+的結構不能做爲左值。

   六、*(cp + 1) 能夠做爲右值,也能夠做爲左值。

   七、++cp 能夠做爲右值,但不能夠做爲左值。 在這個表達式中,咱們增長了指針變量cp的值。表達式的結果是增值後的指針的一份拷貝,由於前綴++先增長它的操做數的值再返回這個結果。這份拷貝的存儲位置並未清晰定義,因此它不是一個合法的左值。
 
  八、cp++ 能夠做爲右值,可是不能夠做爲左值。 後綴++操做符一樣增長cp的值,但它先返回cp值的一份考本而後再增長cp的值。這樣,這個表達式的值就是cp原來的值的一份拷貝。
  
  九、* ++cp 能夠做爲右值,也能夠做爲左值。

  十、*cp++ 能夠做爲右值,也能夠做爲左值。

  十一、++*cp 能夠做爲右值,可是不能夠做爲左值。 因爲這兩個操做符的結合性都是從右向左,因此首先執行的是間接訪問操做,而後,cp所指向的位置的值增長1,表達式的結構是這個增值後的值的一份拷貝。

  十二、(*cp) ++ 能夠做爲右值,可是不能夠做爲左值。

  1三、++*++cp 能夠做爲右值,可是不能夠做爲左值。
express


左值能夠被賦值(被更新),而右值沒有這個限制
好比
const int a=3;
int b=4;
a只能做爲右值,b做爲左右值都可
簡單地說:
左值是放在賦值號左邊的值(能夠改變),右值是放在賦值號右邊的值(沒有限制)
通俗的講,左值就是可以出如今賦值符號左面的東西,而右值就是那些能夠出如今賦值符號右面的東西了。  

舉個很簡單的例子:  

a=b+100;  

那麼這裏a就是左值,b+25就是一個右值。左值和右值之間是不必定都能互換的,上面的這個例子就是不能互換的,若是寫成  

b+100=a;  

你們都能看出來這樣寫會不編譯經過的,由於編譯器沒法判斷b+100的內存地址,因此不能操做。  

看了這個例子,能夠作一個總結,左值必須應該是一個變量或者是表達式等,可是它的物理位置是能夠肯定的,而右值不必定,這也是它們二者之間的區別。  

關於左值是表達式的例子有數組,還有指針這些均可以。  

int array[10];  

int a=5;  

array[a+3]=10; //這裏左值就是一個數組表達式了


通常來講,左值,右值的概念用於賦值表達式。從字面上理解,左值在等號左邊,右值在等號右邊。
左值咱們用的是它的地址,有地址且可地址可用的實體均可以用做左值,右值咱們用的是它的值,只要有一個值,就能夠做爲右值,好比:文字常量,變量。特別的,符號常量(const)對象中的值不能修改,因此只能用做右值。
舉例來講:int a; char b;   a,b均可以做左值。特別的,(a = 5)這個表達式的的值是&a(a的引用),因此它能夠做爲左值。進一步說,若是一個函數返回一個可用的引用,那麼這個函數調用表達式也能夠用做左值,其被賦值的對象是函數返回值所引用的對象。固然,這樣的東西可讀性太差,不推薦使用。
至於右值……基本上全部有值的實體均可以做爲右值
++i是直接給i變量加1,而後返回i自己,由於i是變量,因此能夠被賦值,所以是左值表達式
i++現產生一個臨時變量,記錄i的值,然後給i加1,接着返回臨時變量,而後臨時變量不存在了,因此,不能再被賦值,所以是右值表達式

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寫的這個心得:左值和右值的概念寫代碼寫多了就會天然產生相應感受的——就是飛雪提到的可訪問的存儲。一些感性的例子:
關於左值和右值的Q <wbr>& <wbr>Aa+b這個值是一個至關「懸」的東西,咱們只知道它是a+b 的結果,但不能掌握它的具體狀況,也不太關心它到底怎樣出現甚至是否出現。
關於左值和右值的Q <wbr>& <wbr>A 而*p 就不同,由於 p是個指針,它必然有一個值,無論是有效的仍是無效的,那麼那個值表明的內存區域就確定有某個東西,這是實實在在的。
關於左值和右值的Q <wbr>& <wbr>A
至於&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 thatan expression has anaddressable location that data or information can be put into.)
數組

Q貌似有點明白了,爲何要強調可訪問的存儲?難道還有些擁有存儲卻不可訪問的表達式結果麼?
A
:對的,來看以下例子(來自飛雪):app

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;     int *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爭論了大半夜才弄明白,感謝pmerOFcsupermegaboy。以上內容譯摘自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:數組名,函數,枚舉常量,賦值表達式,強制類型轉換(目標類型是引用時除外),函數調用。this

http://blog.sina.com.cn/s/blog_6a35a0f60100qpnw.htmlspa

相關文章
相關標籤/搜索