起源于C语言
左值和右值都是从继承C语言继承过来的,那么为什么C语言中要创造“左值和右值”,在C语言中又是怎么定义的呢?
在Kernighan Kernighan和Ritchie创造了左值这个术语来区分某些表达与其他表达。在《The C Programming Language 》中他们写到"An object is a manipulatable region of storage; an lvalue is an expression referring to an object....The name 'lvalue' comes from the assignment expression E1 = E2 in which the left operand E1 must be an lvalue expression.",换句话说运算符“=”左边一定是个左值,它最初只是用来做一些区分,编译器报错的时候,我们也会常见到它的身影。
例如:
int n; n = 4;
这里n指向一个整形对象,是一个右值,所以没有错。但是假如我们换成:
4 = n;
编译器就会报错:“error: lvalue required as left operand of assignment 4 = n”;这里4是只是一个常量,不能作为一个左值。
所以,左值和右值可以帮助我们和一些表达式做一些很好的区分,在C语言中,不懂“左值和右值”好像并不影响我们编程,但是它有助于我们深入理解一些内置运算符。
基本的概念
我们已经有几种常用的运算符用到左值,下面我们来看看:
int n; n = 4;
赋值运算符需要一个(非常量)左值作为左侧运算对象,得到的结果仍然是一个左值。
int n = 1, m = 2; n = m + 1; // success m + 1 = n; // error
因为“+”运算符等级高于“=”,所以m+1 = n 等价于 (m + 1) = n ,这里报错,因为m + 1是右值。
int n, *p; ... p = &n; // ok &n = p; // error: &n is an rvalue
这里,“&”地址运算符得到的结果也是右值。
int a[N]; int *p = a; ... *p = 3; // ok *(p + 1) = 4; // ok
这里,“*”解引用运算符得到的结果是左值,所以正确。
到这里,我们可以看出,当一个对象被用作右值的时候,用的是对象的值(内容),而对象被用作左值的时候,用的是对象的地址,如果一个对象没有地址(无法取地址),也就无法作左值。
左值和右值的转化
int a = 1; // a is an lvalue int b = 2; // b is an lvalue int c = a + b; // + needs rvalues, so a and b are converted to rvalues // and an rvalue is returned
一般来说,需要右值的地方可以用左值来代替,但是不能把右值当成左值来用,但是这里有个例外,下面我们来看:
int i = 42; int &&rr2 = i; // error can't bind a rvalue ference to an lvalue int &&rr2 = i *42; // ok
我们可以将一个右值引用绑定到一个表达式上,但是不能讲一个右值引用绑定到一个左值上。
参考: