左值与右值

左值就是那些可以出如今赋值符号左边的东西。
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

相关文章
相关标签/搜索