左值和右值每个程序员都会碰到,但它们究竟是什么以及如何理解它运用它,一开始大家都会感觉比较困难。也许你根本没有想过常见的左值与右值到底有什么本质的区别,拿过来用就是了。还也许会认为等号就是判断左右值的杠杆,中间插个笑话,有师弟说电脑主板上面的南桥北桥与放置的位置相关!
所以我想写这篇总结性的文章(整理性质的)来帮助大家理解它们。希望对大家有帮助。
首先我们需要理解左值和右值的定义:
左值指的是如果一个表达式可以引用到某一个对象,并且这个对象是一块内存空间且可以被检查和存储,那么这个表达式就可以做为一个左值。
右值指的是引用了一个存储在某个内存地址里的数据。
从上面的两个定义可以看出,左值其实要引用一个对象,而一个对象在我们的程序中又肯定有一个名字或者可以通过一个名字访问到,所以左值又可以归纳为:左值表示程序中必须有一个特定的名字引用到这个值。而右值引用的是地址里的内容,所以相反右值又可以归纳为:右值表示程序中没有一个特定的名字引用到这个值除了用地址。
好这些都是从定义上理解左值右值,那么我们再用这些定义作为我们的理论基础来总结一下哪些是左值,哪些是右值:
左值:
Expression | Lvalue |
x = 42 | x |
*ptr = newvalue | *ptr |
a++ | a++ |
b[0] = 100 | b[0] |
const int m = 10 | m |
int& f() | The function call to f() |
右值:
Expression | Rvalue |
100 | 100 |
a * b | The expression of a * b |
int f() | The function call to f() that does not return reference |
以上这些内容都可以用定义来解释为什么这些为左值,而那些为右值。但我要特殊解释一下为什么函数的调用只能作为右值除了这个函数返回的是引用。其实这个也非常好解释,因为如果一个函数返回的值是 内建类型,那么这个返回值是没有办法通过一个名字或者表达式引用到的,同理如果一个函数返回的是一个对象,那么这个对象是一个临时的,也不可能用一个名字访问到。所以函数的调用通常只能作为右值,但如果一个函数返回引用,那么它的返回值就有意义了,因为它是另一个名字的别名,有名字了,所以它就变成了左值。
注意:左值能转化为右值,但反之不行。
好了,讲了这么多我觉得已经足够,但还要多讲一点,这点就是哪些操作符必需左值.
Operator | Requirement |
& (unary) | Operand must be an lvalue. |
++ -- | Operand must be an lvalue. This applies to both prefix and postfix forms. |
= += -= *= %= <<= >>= &= ^= |= | Left operand must be an lvalue. |