左值右值

根据《C++ Primier》P39及其习题的解答,综合总结左值右值的概念如下:

(1)左值:左值就是变量的地址,或者是一个代表“对象在内存中的位置”的表达式。左值可以出现在赋值语句的左边或者右边;

(2)右值:右值就是变量的值,右值只能出现在赋值语句的右边,不能出现在赋值语句的左边,只可在左边。

对于一个变量名来说,其出现在赋值语句的左边,就是一个左值;而出现在赋值运算符右边的变量名或者字面常量就是一个右值。例如:

       val1 = val2/15;

这里的val1就是一个左值,而val2和15就是右值。

1. C++左值与右值概念

变量和文字常量都有存储区,并且有相关的类型,区别在于变量是可寻址的;

对于每个变量,都有2个值与其相关联:

1>数据值,存储在某个内存地址中,也称右值(rvalue),右值是被读取的值(read value),文字常量和变量都可被用于右值。

2>地址值,即存储数据值的那块内存地址,也称左值(lvalue),文字常量不能被用作左值。

2 . C++左值与右值的相关问题

给表达式加上括号: ++a--

结果 ++(a—)//问题:为啥是这样子结合的啊?因为后缀优先级高。

这个表达式是非法的,因为前增量操作要求一个可修改的左值,而 "a--" 不是左值(即右值)

3 . C++左值与右值中前增量和后增量的区别

早期的c语言教材,for循环语句通常写成:

for(int i=0;i<10;i++)

而现在多为:

for(int i=0;i<10;++i)

两者有区别吗?

a++ 即是返回 a的值,然后变量 a 加 1,返回需要产生一个临时变量类似于

  1. {  
  2. int temp = a;  
  3. a=a+1;  
  4. return temp; //返回右值  

 

++a 则为:

  1. {  
  2. a=a+1;  
  3. return &a; //返回左值  

 

显然,前增量不需要中间变量,效率更高。

C++左值与右值的含义与误区

术语 “L-Values” 和 “R-Values” 是很容易被搞混的,因为它们的历史渊源也是混淆。他们最初起源是编译器的设计者,从字面上来理解就是表达式左边的值和表达式右边的值。它们的含义一直在演化而名字却没变,现在已经“名”不副“实”了。虽然还是称为left-value 和right-value,但是他们的含义已经大大不同了。

C++ 03 标准上是这样写的: “每一个表达式要么是一个 lvalue,要么就是一个 rvalue。”

记住,lvalue和rvalue是针对表达式而言的。

lvalue 是指那些单一表达式结束之后依然存在的持久对象。例如: obj,*ptr, prt[index], ++x 都是 lvalue。

rvalue 是指那些表达式结束时(在分号处)就不复存在了的临时对象。例如:1729 , x + y , std::string("meow") , 和 x++ 都是 rvalue。

++x 和 x++ 的区别的语义上的区别: 当写 int i = 10 ; 时, i 是一个 lvalue,它实际代表一个内存里的地址,是持久的。 表达式 ++x 也是一个 lvalue,它修改了 x 的值,但还是代表原来那个持久对象。但是,表达式 i++ 却是一个 rvalue,它只是拷贝一份i的初值,再修改i的值,最后返回那份临时的拷贝,那份拷贝是临时对象。 ++i 和 i++ 都递增i,但 ++i 返回i本身,而 i++ 返回临时拷贝。这就是为什么 ++i 之所以是一个 lvalue,而 i++ 是一个 rvalue。

lvalue 与 rvalue 之分不在于表达式做了什么,而在于表达式代表了什么(持久对象或临时产物)。 判断一个表达式是不是 lvalue 的直接方法就是“能不能对表达式取址?”,如果能够,那就是一个 lvalue;如果不能,那就是一个 rvalue。

关于左值右值的一个问答:

问:C++中 int a=1; 为什么a++++不可以而++++a可以 请具体说明,可以的话请指出++操作符左值与右值的差别。

答:

其实这取决于++左结合操作符号的操作函数,楼主可以研究下:编译器中对于++a的调用相当于
int operator++ (int)
而++右操作符操作函数时,相当于这样,返回的依然是一个int型,所以无论++在a的左边多少个都是可以的。
const int operator++()
注意这里返回的是一个const的,const只能作为右值,而不能作为左值的。所以a++是可以的,但是a++++就不行,因为a++返回的是一个const的int值,而该值是不能改变的,所以a++++不行。

但是需要重点指出的是:*取值操作是可以针对右值的。比如:

	int temp=10;
	int *p=&temp;
	int temp1 = *(int*)(0x0012ff7c);//0x0012ff7c是temp的地址
	cout<< temp1 <<endl;

*操作符的操作数不需要是左值,只需要他表示一个合法可用的地址值,比如*(p+1)的操作数明显是右值,还可以直接使用地址值,int a=*(int*)0x12ff50,只要后面这个地址有效就可以 .而表达式*p或者*(p+1)他们才是左值,即*(p+1)=5;是合法的,表示p+1这个地址处存在的一个对象,使之等于5 。

总之,能作为赋值符=的左边部分  的表达式,就是左值。不仅仅限于变量名,*p,arr[i],返回引用的函数返回值等表达式也是是左值。

  

以下内容转自网友博客:http://blog.csdn.net/csdnji/article/details/169200

左值(lvalue)和右值(rvalue)是编程中两个非常基本的概念,但是也非常容易让人误解,看了很多文章,自我感觉真正将这个问题讲的很透彻的文章还没有看见,所以自告奋勇来尝试一下。如果左值右值的概念不是非常清楚的话,它们迟早会像拦路虎一样跳出来,让你烦心不已,就像玩电脑游戏的时候每隔一段时间总有那么几个地雷考验你的耐性,如果一次把所有地雷扫尽就好了。:)

左值(lvalue)和右值(rvalue)最先来源于编译理论(感谢南大小百合的programs)。在C语言中表示位于赋值运算符两侧的两个值,左边的就叫左值,右边的就叫右值。比如:

int ii = 5;//ii是左值,5是右值

int jj = ii;//jj是左值,ii是右值

上面表明,左值肯定可以作为右值使用,但反之则不然。左值和右值的最早区别就在于能否改变。左值是可以变的,右值不能变。【注1

1:这一点在C++中已经猪羊变色,不再成立。拱猪游戏还是挺好玩的,我还真抓过好几次全红心,不过真的好险。:)

在很多文章中提到,在C++中,左值更多的指的是可以定位,即有地址的值,而右值没有地址。【注2

2:这一点仍然不准确,我在程序中生成一个临时右值std::vector(),你能够说它没有地址吗?难道它是没有肉体的鬼魂或幽灵?它是有地址的,而且它也是绝对的右值。

在现代C++中,现在左值和右值基本上已经失去它们原本所具有的意义,对于左值表达式,通过具体名字和引用(pointer or reference)来指定一个对象。非左值就是右值。我来下一个定义:

左值表示程序中必须有一个特定的名字引用到这个值。

右值表示程序中没有一个特定的名字引用到这个值。

跟它们是否可以改变,是否在栈或堆(stack or heap)中有地址毫无关系。

1.左值

在下面的代码中:

int ii = 5;

int const jj = ii;

int a[5];

a[0] = 100;

*(a+3) = 200;

int const& max( int const& a, int const& b ) //call by reference

{

      return a > b ? a : b;

}

int& fun(int& a) //call by reference

{

      a += 5;

   return a;

}

iijja[0]*(a+3),还有函数max的返回值比如max(ii, jj),【注3】函数fun的返回值fun(ii)都是左值。,它们都是有特定的引用名字的值。iijja[0]*(a+3)max(ii, jj)fun(ii)分别就是它们的名字。

3:在这里有一个不太容易分清楚的盲点。那就是有人会问max(8, 9)到达是左值还是右值,C++标准规定常量引用(reference to const)可以引用到右值,所以max(8, 9)似乎应该是右值,不过不管它是左值,还是右值,我们都不能试图去改变它。为了与前面的概念一致,我认为它是左值,不可改变的常量左值。

左值有不能改变的,即被const所修饰的左值,比如上面的jjmax(ii, jj)都是被常量(const)魔咒所困住的左值。

没有被const困住的左值当然是可以改变的,比如下面的代码都是成立的:

ii = 600;

a[0] = 700;

fun(ii) = 800; //OK!

我们的眼睛没有问题,fun(ii) = 800;完全正确,因为它是可以改变的左值【所以这里需要尤其注意,如果返回值是指针或者引用的话,最好要申明成const类型】。所以我们看STL的源码,就会理解std::vector中的重载operator[]运算符的返回值为什么要写成引用,因为operator[]必须返回左值。

 

2.右值

没有特定名字的值是右值。先看下面的代码:

std::list();

std::string(“It is a rvalue!”);

int fun1() //call by value

{

      

}

int* fun2() //call by reference

{

      

}

其中std::list()std::string(“It is a rvalue!”),函数fun1的返回值fun1(),函数fun2的返回值fun2()都是右值,它们的值都没有特定的名字去引用。也许有人会奇怪,fun2()也是右值?最前面的max(a,b)不是左值吗?

请看清楚,函数fun2的返回值是pointerpointer也是call by value,而函数max的返回值是referencereferencecall by reference。所以说C++中引入reference不仅仅是为了方便,它也是一种必须。【注4

4Scott Meyer写的《More Effective C++》的条款1专门讲了pointerreference的区别,写的很好,辨别的非常清楚。

fun2()是右值,但 *fun2()却是左值,就跟经常看到的*p一样,所以看C++库代码的时候,会发现重载operator*的函数返回值是reference

当然我还遗漏了一种右值,那就是字面上的(literal)值,比如58.23’a’等等理所当然的都是右值。

右值最初出现的时候,一个最大的特征就是不可改变。但就跟我们的道德标准一样,时代不同了,标准也变化了,以前的三纲五常早已经被扔到历史的垃圾堆里面了。

C++中有可以改变的右值,而且这个特性还非常有用。那就是用户自定义的类(class)的构造函数生成的临时对象。比如:

std::vector(9)std::deque(),……都是可以改变的右值。在Herb Sutter的《More Exceptional C++》中的条款7page51页有这样几行代码:

// Example 7-2(b): The right way to shrink-to-fit a vector.

vector<Customer> c( 10000 );

// ...now c.capacity() >= 10000...

// erase all but the first 10 elements

c.erase( c.begin()+10, c.end() );

// the following line does shrink c's

// internal buffer to fit (or close)

vector<Customer>( c ).swap( c );

// ...now c.capacity() == c.size(), or

// perhaps a little more than c.size()

认真看几遍,你会发现但vector的大小增大到一定程度,你又用不着这么多空间的时候,你会想办法把它收缩到最合适的大小,但利用别的办法比如调用成员函数reserve()都无法办到,这个时候就必须利用右值可以改变这个性质了。

vector<Customer>( c ).swap( c );这行代码就是点睛之处。

首先使用复制构造函数生成临时右值vector<Customer>( c ),这个右值正好是合适大小,然后和c交换【注5】,c就变成合适大小了,最后在整个表达式结束的时候,这个临时右值析构归还内存空间。真是绅士一般的优雅!

5:这个时候这个临时右值就发生了改变。

如果还不理解,可以看看书,或者直接看库的源代码。

至于为什么会这样?我思考了一下,我想是这样的,我们看类(class)的数据布置结构,会发现它的每一个数据成员都是有名字的,我想编译器在编译的过程中,都会生成一个外部不所知的对这个临时对象右值的名字引用,但需要改变这个临时对象的时候,这个名字就用上了。比如:

class Point

{

public: //纯粹为了方便,我把数据成员公开,现实中尽量不要这样用

      int x, y ,z;

      ……//其他各种成员函数

};

我们现在就可以改变右值,用到了匿名的引用名字。

Point().x = 6;//改变了右值

Point().y = 6;//同意改变了右值,不过注意,这个右值跟上面的不是同一个。

总结

左值和右值的真正区别我想就是这些了,左值表示有特定的名字引用,而右值没有特定的名字引用。当然我仍然会有疏忽,希望大家能够提醒我,指正我的不足。

前两天看Herb Sutter从邮件中寄来的新文章(我订阅他的新文章邮件通知),一篇是讲Tuple数据结构的,没有什么新意,以前好像看过,还有一篇名字是:(MostlyPrivate,地址为http://www.cuj.com/documents/s=8273/cujcexp2107sutter/ 内容本身并不深,但看完文章,发现随处可见C++的波诡云谲,又会对什么叫袖里乾坤,滴水藏海多一份感性认识。

在下一篇文章我想从不同于一般的角度,从自己的经历谈谈在校毕业生在IT行业怎样找工作,我想会让所有读者都有一些思考,不仅仅是求职者。题目我已经想好了,就叫《扮虎吃猪》,不过现在我有一些别的事情要忙,所以可能会让大家等几天。

转载请注明来源,谢谢!

吴桐写于2003.6.20

最近修改2003.6.21

原文地址:https://www.cnblogs.com/jiayouwyhit/p/3238548.html