左值右值
2010-12-28 00:25
225 查看
++a是先自增,然后返回增加后的变量a,a是变量,本来就可以做左值,所以(a++)++正确;a++是后自增——在表达式里值还是原来的值——返回的是一个等于a+1的临时变量,临时变量是有const属性的,不可以更改,所以(a++)++错误。
左值和右值
左值(left value):出现在赋值表达式的左边的表达式,其作用为存放数据空间,且存放是允许的。缩写:lvalue
右值(right value):只能出现在赋值表达式的右边。缩写:rvalue
左值也可以作为右值表达式,变量可以是左值,也可以为右值,但常量只能是右值。
a=b // b是变量,在这里是左值,也可以作为右值
a=8 // 8是常量,只能作为右值
(a=4)=28 // C++语法规定:a=4为左值,28为右值
操作符的优先级与结合性
c = a + b // OK
c = a + + b // Error
c = a + + + b // OK a ++ + b
c = a + + + + b // Error a ++ ++ b
c = a + + + + + b // Error a ++ ++ + b
操作符自左向右取尽可能多的符号组成运算符,有些操作符的结合性是自右向左的,操作符的其结合性以下表为准。
class Couter{
private:
int number;
public:
Couter& operator++(int);
..............
};
对于前缀表达式:
Couter& Couter::operator++()//返回引用
{
number++;
return *this;
}
这样没错,但它的功能为<对a自加后,之作用在a自身>,
要实现后缀式,这必须用
Couter Couter::operator++(int)//后缀式,不返回引用
{
Couter temp;
temp=*this;
number++;
return temp;
}
这样才能实现先取出a的值进行操作,绕后再对a自加,但同时带来的
弊端为a自加后,重载算符函数的返回值作用不到a自身,所以不可以
复合使用.即使编译可以通过,但结果肯定不对!
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,返回需要产生一个临时变量类似于
++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。
什么情况下返回指针?什么情况下返回引用?指针和引用的效果一样么?
这里不仅涉及到引用的实现,也涉及到左值与右值的概念。因为返回值性质的不同决定了引用与指针必定不是相同的。相信你读过我写的这篇文章之后,会有一个比较清醒的认识。
左值(lvalue)和右值(rvalue)最先来源于C语言。最先在C语言中表示位于赋值运算符两侧的两个值,左边的就叫左值,右边的就叫右值。
比如:
int ii = 5; //ii是左值,5是右值
int jj = ii; //jj是左值,ii是右值
上面表明,左值肯定可以作为右值使用,但反之则不然。左值和右值的最早区别就在于能否改变。左值是可以变的,右值不能变,但是这一点在C++中已经不再成立。
在现代C++中,现在左值和右值基本上已经失去它们原本所具有的意义,对于左值表达式,通过具体名字(variant name)和引用(reference)来指定一个对象。非左值就是右值。我们来下一个定义:
左值表示程序中必须有一个特定的名字引用到这个值。
右值表示程序中没有一个特定的名字引用到这个值。
这跟它们是否可以改变,是否在栈或堆(stack or heap)中毫无关系。
1.左值
在下面的代码中:
int ii = 5;
const int jj = ii;
int a[5];
a[0] = 100;
*(a+3) = 200;
const int& max(const int& a, const int& b) //call by reference
{
return a > b ? a : b;
}
int& fun(int& a) //call by reference
{
a += 5;
return a;
}
ii,jj,a[0],*(a+3)这些值,还有函数max的返回值比如max(ii, jj),函数fun的返回值fun(ii)都是左值。因为它们都是被特定的名字所引用的值。
ii,jj,a[0],*(a+3),以及引用了max和fun的返回值的变量就是它们的名字。
注:有人会问max(8, 9)到底是左值还是右值,C++标准规定常量引用
(reference to const)可以引用到右值,所以max(8, 9)似乎应该是右值。但根据我们对左值的定义,说它是左值也对。不管它是左值,还是右值,我们都不能试图去改变它。为了与前面的概念一致,姑且认为它是左值,不可改变的常量左值。
左值有不能改变的,即被const所修饰的左值,比如上面的jj,max(ii, jj),没有被const困住的左值当然是可以改变的。
比如下面的代码都是成立的:
ii = 600;
a[0] = 700;
fun(ii) = 800; //OK!
fun(8);//错,必须传递变量来初始化引用,如果参数是const int&就可以。
我们的眼睛没有问题,fun(ii) = 800;完全正确,因为它是可以改变的左值。
2.右值
没有特定名字的值是右值。
看下面的代码:
int fun1() //call by value
{
…
}
int* fun2() //call by value
{
…
}
函数fun1的返回值fun1(),函数fun2的返回值fun2()都是右值,它们的值都没有特定的名字去引用。
也许有人会奇怪,fun2()也是右值?前面说的max(a,b)不是左值吗?请看清楚,函数fun2的返回值是pointer,pointer也是call by value,而函数max的返
回值是reference,reference是call by reference。
当然字面上的(literal)值,比如5,8.23,’a’等等也都是右值。
右值最初出现的时候,一个最大的特征就是不可改变。。但时代不同了,标准也变化了。
C++中有可以改变的右值。那就是用户自定义的类(class)的构造函数生成的临时对象。
至
于原因,我思考了一下,我想是这样的:我们看类(class)的数据布置结构,会发现它的每一个数据成员都是有名字的,我想编译器在编译的过程中,都会生
成一个外部不所知的对这个临时对象右值的名字引用。但当需要改变这个临时对象的时候,这个名字就用上了(实际这时变成了左值,因为有了名字引用)。
比如:
class Point
{
public:
int x;
……//其他各种成员函数
};
我们现在就可以改变右值,用到了匿名的引用名字。
Point().x = 6;//改变了右值
最后说一下引用的机制。很多人都在争论引用是怎么实现的?如果你看看vc做出的反汇编代码,你会发现在机器码的层次,指针与引用的实现是相同。
但是,如果你以此断定引用就是指针,那么就错了。的确,引用是靠指针实现的,但是二者有区别。我们讨论引用的问题,一定要在c++的语言层面上讨论,而不
能跑到汇编代码中去看。在c++中,引用就是个别名,本身不会另外分配空间。但是在汇编层次,你会发现“引用”有自己的空间。别忘了,你已经离开了c++
的范围,这样的讨论失去了意义。总之,我们应该明白,c++中的引用就是个别名,具体的实现方法也要取决于编译系统和优化方案。
左值和右值
左值(left value):出现在赋值表达式的左边的表达式,其作用为存放数据空间,且存放是允许的。缩写:lvalue
右值(right value):只能出现在赋值表达式的右边。缩写:rvalue
左值也可以作为右值表达式,变量可以是左值,也可以为右值,但常量只能是右值。
a=b // b是变量,在这里是左值,也可以作为右值
a=8 // 8是常量,只能作为右值
(a=4)=28 // C++语法规定:a=4为左值,28为右值
操作符的优先级与结合性
c = a + b // OK
c = a + + b // Error
c = a + + + b // OK a ++ + b
c = a + + + + b // Error a ++ ++ b
c = a + + + + + b // Error a ++ ++ + b
操作符自左向右取尽可能多的符号组成运算符,有些操作符的结合性是自右向左的,操作符的其结合性以下表为准。
class Couter{
private:
int number;
public:
Couter& operator++(int);
..............
};
对于前缀表达式:
Couter& Couter::operator++()//返回引用
{
number++;
return *this;
}
这样没错,但它的功能为<对a自加后,之作用在a自身>,
要实现后缀式,这必须用
Couter Couter::operator++(int)//后缀式,不返回引用
{
Couter temp;
temp=*this;
number++;
return temp;
}
这样才能实现先取出a的值进行操作,绕后再对a自加,但同时带来的
弊端为a自加后,重载算符函数的返回值作用不到a自身,所以不可以
复合使用.即使编译可以通过,但结果肯定不对!
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,返回需要产生一个临时变量类似于
{ int temp = a ; a a =a+1; return temp; //返回右值 }
++a 则为:
{ a a =a+1; 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。
什么情况下返回指针?什么情况下返回引用?指针和引用的效果一样么?
这里不仅涉及到引用的实现,也涉及到左值与右值的概念。因为返回值性质的不同决定了引用与指针必定不是相同的。相信你读过我写的这篇文章之后,会有一个比较清醒的认识。
左值(lvalue)和右值(rvalue)最先来源于C语言。最先在C语言中表示位于赋值运算符两侧的两个值,左边的就叫左值,右边的就叫右值。
比如:
int ii = 5; //ii是左值,5是右值
int jj = ii; //jj是左值,ii是右值
上面表明,左值肯定可以作为右值使用,但反之则不然。左值和右值的最早区别就在于能否改变。左值是可以变的,右值不能变,但是这一点在C++中已经不再成立。
在现代C++中,现在左值和右值基本上已经失去它们原本所具有的意义,对于左值表达式,通过具体名字(variant name)和引用(reference)来指定一个对象。非左值就是右值。我们来下一个定义:
左值表示程序中必须有一个特定的名字引用到这个值。
右值表示程序中没有一个特定的名字引用到这个值。
这跟它们是否可以改变,是否在栈或堆(stack or heap)中毫无关系。
1.左值
在下面的代码中:
int ii = 5;
const int jj = ii;
int a[5];
a[0] = 100;
*(a+3) = 200;
const int& max(const int& a, const int& b) //call by reference
{
return a > b ? a : b;
}
int& fun(int& a) //call by reference
{
a += 5;
return a;
}
ii,jj,a[0],*(a+3)这些值,还有函数max的返回值比如max(ii, jj),函数fun的返回值fun(ii)都是左值。因为它们都是被特定的名字所引用的值。
ii,jj,a[0],*(a+3),以及引用了max和fun的返回值的变量就是它们的名字。
注:有人会问max(8, 9)到底是左值还是右值,C++标准规定常量引用
(reference to const)可以引用到右值,所以max(8, 9)似乎应该是右值。但根据我们对左值的定义,说它是左值也对。不管它是左值,还是右值,我们都不能试图去改变它。为了与前面的概念一致,姑且认为它是左值,不可改变的常量左值。
左值有不能改变的,即被const所修饰的左值,比如上面的jj,max(ii, jj),没有被const困住的左值当然是可以改变的。
比如下面的代码都是成立的:
ii = 600;
a[0] = 700;
fun(ii) = 800; //OK!
fun(8);//错,必须传递变量来初始化引用,如果参数是const int&就可以。
我们的眼睛没有问题,fun(ii) = 800;完全正确,因为它是可以改变的左值。
2.右值
没有特定名字的值是右值。
看下面的代码:
int fun1() //call by value
{
…
}
int* fun2() //call by value
{
…
}
函数fun1的返回值fun1(),函数fun2的返回值fun2()都是右值,它们的值都没有特定的名字去引用。
也许有人会奇怪,fun2()也是右值?前面说的max(a,b)不是左值吗?请看清楚,函数fun2的返回值是pointer,pointer也是call by value,而函数max的返
回值是reference,reference是call by reference。
当然字面上的(literal)值,比如5,8.23,’a’等等也都是右值。
右值最初出现的时候,一个最大的特征就是不可改变。。但时代不同了,标准也变化了。
C++中有可以改变的右值。那就是用户自定义的类(class)的构造函数生成的临时对象。
至
于原因,我思考了一下,我想是这样的:我们看类(class)的数据布置结构,会发现它的每一个数据成员都是有名字的,我想编译器在编译的过程中,都会生
成一个外部不所知的对这个临时对象右值的名字引用。但当需要改变这个临时对象的时候,这个名字就用上了(实际这时变成了左值,因为有了名字引用)。
比如:
class Point
{
public:
int x;
……//其他各种成员函数
};
我们现在就可以改变右值,用到了匿名的引用名字。
Point().x = 6;//改变了右值
最后说一下引用的机制。很多人都在争论引用是怎么实现的?如果你看看vc做出的反汇编代码,你会发现在机器码的层次,指针与引用的实现是相同。
但是,如果你以此断定引用就是指针,那么就错了。的确,引用是靠指针实现的,但是二者有区别。我们讨论引用的问题,一定要在c++的语言层面上讨论,而不
能跑到汇编代码中去看。在c++中,引用就是个别名,本身不会另外分配空间。但是在汇编层次,你会发现“引用”有自己的空间。别忘了,你已经离开了c++
的范围,这样的讨论失去了意义。总之,我们应该明白,c++中的引用就是个别名,具体的实现方法也要取决于编译系统和优化方案。
相关文章推荐
- 左值右值问题
- c++左值与右值的含义与误区
- 关于i++和++i以及左值,右值
- 自增自减运算符,左值右值
- The New C++ -- 变量 (7. 左值,右值)
- c中左值,右值
- c/c++中的lvaues(左值)和rvalues(右值)
- 关于左值与右值(lvalue & rvalue)
- C++左值右值引用
- 赋值运算符、左值与右值
- C++左值引用和右值引用
- [C/C++不常见语法特性]_[初级]_[左值-右值-lvalue-rvalue]
- 左值和右值/auto ptr智能指针
- C++11左值右值、转移语义
- 左值和右值.cpp
- 左值引用"&"、右值引用"&&"以及它们之间的转换
- C++左值右值和移动语义
- C++11之左值、纯右值和将亡值
- 左值与右值
- [转]C++11 左值、右值、右值引用详解