理解lvalues和rvalues在c/c++

阳光穿透心脏的1/2处 2023-07-07 11:26 21阅读 0赞

在看 Eli Bendersky的文章的时候,记录一下。

引言

左值和右值在编程中不是常遇见,但出现时候,不能很快理解,所以这次做一个总结。

  1. int foo()
  2. { return 2; }
  3. int main()
  4. {
  5. foo()=2;
  6. return 0;
  7. }

gcc编译这段代码

cpp error: lvalue required as left operand of assignment
foo()=2; 左值需要一个左运算符

再用g++编译

  1. int& foo(){ return 2;}

错误,右值错误。

定义

1,定义一个左值:表示在存储器中占用一些可识别的内存空间的对象。
2,右值用排除法来定义,每个表达式都是左值,否则是右值。所以知道rvalue是一个表达式,不表示对象,不再内存占可标识的位置空间

例子

  1. int var;
  2. var=4;

一个赋值操作,期望一个左值是左操作数,var就是一个左值(在存储器中占可标识的空间)。
下面这个就是错误的:

  1. 4=var //wrong
  2. var+1)=4 //wrong

因为,对于4和(var+1),他们都不是左值(不在存储器中占可标识的空间),那么他们就成了右值(在计算期间仅驻留某个临时存储器中)都是表达式的临时结果,没有可识别的存储位置。
现在知道了第一段代码为什么错误了,因为foo函数返回了一个临时值,一个右值,foo()=2在编译器期望他应该是一个左值,能够被2赋值!
但是,并不是对所有的函数返回值调用分配都是无效的,c++的引用使得有可能
如:
int globalvar=20; int &foo(){ return globalvar; } int main() {foo()=10 ; return 0}注意:此处foo返回一个引用,他是一个lvalue。
对于c++返回左值的能力多用于重载某些运算符。常见用例是在重载方括号运算符 “[ ]” 时,如:

  1. std::map<int, float> mymap;
  2. mymap[10]=5.5;

在这里将5.5赋值给mymap[10]起作用是因为std::map::operator [] 的非常量的重载返回引用。

可修改的左值

c定义的时候,是:适用于赋值左侧的值。但是在引入const关键字后,由const定义的左值变得不可修改了。
const int a=10;
a=4; //无法分配修改了

左值和右值之间的转换

一般,对对象的值进行操作的语言在构造时候都需要使用右值(不再存储器中占可标识空间的值,将亡值)作为参数。如“+”将两个右值作为参数,返回一个右值最后赋值给左值。

  1. int a=1;
  2. int b=2; //a,b是左值
  3. int c=a+b; //+需要右值,所以将ab转换成右值,并返回右值

上述进行了隐式的转换,左值——》右值
反过来呢?、将右值转换为左值。但是必须要显示地分配。绝对不可能隐式的转换右值变为左值。
使用“ * ”解引用运算符可以产生左值,将右值作为参数

  1. int arr[]={ 1,2};
  2. int * p= &arr[0]; //&将左值变成右值
  3. * (p+1)=10; //正确,P+1是一个右值,但是*(p+1)是一个左值。

显式转换。
注意:“ &” 运算符取地址,采用的是左值作为参数!!!所以参数不能输入右值
如:

  1. int var=10;
  2. int * bad_addr=&(var+1); //这是明显错误的,因为&参数只能是左值,才能转换为右值。
  3. 正确做法:
  4. int *p=&var;
  5. 错误:
  6. &var=38 //&var 是一个右值!

这只是左值右值的冰山一角,还有很多值得去学习的。

发表评论

表情:
评论列表 (有 0 条评论,21人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C++ rvalue赋值运算符定义

    C++ 右值赋值运算符定义,这种操作符的函数在什么情况会发生呢? Obj a = 临时对象(这种临时对象一般是函数的返回对象,返回这个对象进行赋值然后销毁),所以这种是右值(