`
xpwps21p
  • 浏览: 15753 次
最近访客 更多访客>>
社区版块
存档分类
最新评论

关于左值和右值的Q & A(转)

 
阅读更多

关于左值和右值的Q & A(转)
2010年11月21日
  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 anaddressable 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: 数组名,函数,枚举常量,赋值表达式,强制类型转换(目标类型是引用时除外),函数调用。小结的小结:要确认一个表达式是否左值,总的来说要按规则来。要理解记忆这些规则,可以按“拥有可访问的存储“来。但是,其实不用记得太清或太深究,错了编译器会有提示的。
分享到:
评论

相关推荐

    左值与右值的引用

    这是关于C++左值与右值引用的课程视频,课程易于理解,可以很快掌握

    C语言指针以及区分左值和右值的技巧(整理)

    看了一下自己的资源,下载了很多,实在不好意思,整理一...也有一些注释和精简。 C语言主要就是指针,其中的三篇文章是我阅读相关内容获得最大启发的三篇文字。都是baidu搜索来的,希望你也能抽空把C语言的基础打扎实。

    左值和右值

    左值和右值

    关于i++和++i以及左值,右值

    本文主要讲了关于i++和++i以及左值,右值的问题,希望对你的学习有所帮助。

    C++中的左值和右值

    在C/C++中,左值(lvalue)和右值(rvalue)是用于规定表达式(expression)的性质。C++中表达式要不然是左值,要不然是右值。  这两个概念在C语言中比较容易理解:左值能放在赋值语句的左边,右值不能。但是当来到C++时...

    左值,右值,引用,以及源代码

    左值,右值,引用,以及源代码

    C语言程序设计(第2版)-2期 拓展知识3-1 左值和右值.pdf

    C语言程序设计(第2版)-2期 拓展知识3-1 左值和右值.pdf 学习资料 复习资料 教学资源

    C++11中的左值引用和右值引用

    1.首先区分左值和右值  左值是表达式结束后依然存在的持久对象  右值是表达式结束时不再存在的临时对象  便捷方法:对表达式取地址,如果能,则为左值,否则为右值  举例:  int a = 10  int b = 20  ...

    cpp代码-左值 和 右值 ---- 左值引用(就是别名) 和 右值引用

    cpp代码-左值 和 右值 ---- 左值引用(就是别名) 和 右值引用

    浅谈C++左值引用和右值引用

    这篇blog主要介绍一下左值和右值的概念及应用陷阱 1、左值和右值的概念 左值是可以放在赋值号左边可以被赋值的值;左值必须要在内存中有实体; 右值当在赋值号右边取出值赋给其他变量的值;右值可以在内存也可以在...

    C++中关于左值和右值的讨论

    (某些情况下,右值表达式也能引用(refer)到某一个对象,并且可能间接修改该对象的值,后述)。何谓对象?如果没有明确说明,这里说的对象,和狭义的类/对象(class/object)相比,更为广泛。在C/C++中,所谓的...

    非常量引用的初始值必须为左值的问题

    非常量引用的初始值必须为左值的问题 C++ 11中引入的一个非常重要的概念就是右值引用。理解右值引用是学习“移动语义”(move ... 对左值和右值的一个最常见的误解是:等号左边的就是左值,等号右边的就是右值。

    c++的左值右值 i++与++i的区别.docx

    c++的左值右值 i++与++i的区别.docx

    31_c++中的左值引用与右值引用1

    左值:可以取地址的,有名字的,临时的右值:不能取地址的,没有名字的,临时的举个栗: int a = b + c ,a 就是左值,其变量名为 a ,通过 &a 可

    浅析C++11中的右值引用、转移语义和完美转发

     C++对于左值和右值没有标准定义,但是有一个被广泛认同的说法:可以取地址的,有名字的,非临时的就是左值;不能取地址的,没有名字的,临时的就是右值.  可见立即数,函数返回的值等都是右值;而非匿名对象(包括变量),...

    C++11新特性——标准库篇1

    1.1. 左值和右值 1.2. 右值引用 1.3. 右值引用语法

    C++11中的右值引用

    在C++98中有左值和右值的概念,不过这两个概念对于很多程序员并不关心,因为不知道这两个概念照样可以写出好程序。在C++11中对右值的概念进行了增强,我个人理解这部分内容是C++11引入的特性中难以理解的了。该特性...

Global site tag (gtag.js) - Google Analytics