登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

天道酬勤 玩物丧志

用勇气去改变可以改变的事情,用胸怀去包容无法改变的事情,用智慧去判断两者的区别

 
 
 

日志

 
 

关于左值 右值的一些研究  

2014-01-05 20:59:49|  分类: C/C++ |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

  中国人的优良传统总是喜欢考一些看起来稀奇古怪但是实际上有没有什么用的一些题目,从而来显示自己出题水平的高超,深以难倒广大考生为乐,就比如这一道坑爹的题目:贴上代码:

  

int i=3;
int a=(i++)+(i++);
a=?

 看到这种题目我就瞬间抓狂了,说句实话我是真心佩服那帮中国所有的出题人,怎么恶心人怎么出,真心我去年买了个表。

  对于这种未定义行为的c语言我是不赞成的,本身的定义都模糊的东西怎么可以拿出来用呢,这不是明显逻辑上的犯抽吗?尽管对这种题目很蛋疼,但是做为一个万年中国应试教育下的孩子,我还是对这种题目打算好好研究下,当然,如果我要是这么研究下去就和我题目的主题不符了,我这里拐了个弯,我先研究了下关于运算符优先级的问题,尽管这个表达式的优先级看起来是那么的一目了然。

  我先随机抽取了几个单目运算符的表达式,贴代码:gcc 编译

 

int a=9;
int *p=&++a;
int tmp=*&a;
int tmp_1=!--++a;
int tmp_2=--++a;
int tmp_4=--a++;
int tmp_3=!++a;

 结果报错,有几条错误如下,错误结果如下:lvalue required as unary ‘&’ operand;lvalue required as decrement operand ,翻译过来就是在自加运算和&一元操作中必须要为左值,很好我们现在终于来到了我们的主题,对于左值和右值的区别,我们老师以前模糊的和我们有过简单的交流,左值就是赋值操作符左边的数,是可以进行修改的,右值是赋值操作符右边的数,是不可以修改的,同样的左值也不一样在赋值操作符的左边,也可以在右边,

  比如    int i=2;

     int j=i;

很明显这么简单的解释根本无法解释上述的错误,左值和右值依旧是拦路虎。

经过网上的资料参考,消化吸收后,真心感谢网上的开源精神,看到这么一句话——赋值操作符、自增/自减操作符 (++/–)、取地址操作符 (&,注意该操作符同时要求操作数不能是bit field,以及不能使用register进行修饰) 等要求其操作数必须是左值。对应我上述代码,颇有隔靴搔痒之嫌。几番研究,恍然大悟。贴code

int i=0;
(++i)=5;
(i++)=5;

  编译错误已出;lvalue required as left operand of assignment 赋值左边的操作数要求是左值,我们首先确定了i是一个左值,进行自增自减当然是可以的不会报错,但是自增表达式的值不是一个左值,那么赋值操作符当然无法通过,要求左边必须是左值,当然这种情况在c++中因为运算符重载的关系已经变过了,我们这里不加以引申,后期如果可以的话我还会加以讨论,我们这里只进行对于C语言的研究。

  我姑且认为自增表达式中我们得到的值应该是i的一份临时拷贝,只不过i++ 与++i 一个是返回i的拷贝然后才进行i的对i这个操作数的加1,一个是加1之后返回这个数的拷贝,都是一份临时的拷贝,我们并不知道这个表达式的值放在内存的什么地方,也无法保证表达式的值一次还会放在他的老地方,所以++是一个返回右值的表达式。

   明白了这个关系,那么我们用一些式子来加深对左值和右值的概念

 

  

  

     char cha='a';
    char *p=&cha;
    *p='a';
    char tmp=*p;
    char tmp_1=*p+1;
    char tmp_2=*(p+1);
    *(p+1)='b';
    char tmp_3=(*p)+1;
    char *tmp_4=++p;
    char *tmp_5=p++;
    char tmp_6=*++p;
    *++p='c';
    char tmp_7=*p++;
    *p++='d';
    char tmp_8=++*p;
//++*p='d';
    char tmp_9=(*p)++;
//(*p)++='c';
    char tmp_10=++*++p;
//++*++p='c';
    char **p1=&p;
    char tmp_11=*++*p1;
    *++*p1='c';
//char tmp_12=*++++p;
//*++++p='a';

  

这些式子摘自http://blog.sina.com.cn/s/blog_8af6bbd10100ybid.html 特此声明。明白了这些式子相信对于左右值理解更加深刻。暂时就说这么多,以后关于c++的继续补充。http://www.cnblogs.com/chongxin/p/3226397.html

   左值就是那些能够出现在赋值符号左边的东西。
   右值就是那些可以出现赋值符号右边的东西。
   例如: 
        a = b + 25;
      a就是一个左值,因为它标识了一个可以存储结果值的地点,b + 25是个右值,因为它指定了一个值。但是它们可以互换吗? b + 25 = a; 原先用作左值的a此时也可以当作右值,因为每个位置都包含一个值。然而,b + 25不能作为左值,因为它并未标识一个特定的位置。因此,这条赋值语句是非法的。
      注意当计算机计算b + 25时,它的结果必然保存于机器的某个地方。但是,程序员并没有办法预测该结果会存储在什么地方,也无法保证这个表达式的值下次还会存储于那个地方。其结果是,这个表达式不是一个左值。基于同样的理由,字面值常量也都不是左值。
      听上去似乎是变量可以作为左值而表达式不能作为左值,但这个推断并不准确。在下面的赋值语句中,左值便是一个表达是式。
     int a[30];
     ...
     a[b + 10] = 0;
     下标引用实际上是一个操作符,所以表达式的左边实际上是表达式,但它却是一个合法的左值,因为它标识一个特定的位置,我们以后可以在程序中引用它。这里另外一个例子:
     int a, *pi;
     ...
     pi = &a;
     *pi = 20;
     第2条赋值语句,它左边的那个值显然是一个表达式,但它却是一个合法的左值。为什么?指针pi的值是内存中某个特定位置的地址,*操作符使机器指向那个位置。当它作为左值使用时,这个表达式指定需要进行修改的位置。当它作为右值使用时,它就是提取当前存储于这个位置的值。
     指针中的左右值举例:
     char cp = 'a';
     char *cp = &ch;
     1、&ch 可以作为右值,但是不能作为左值。为什么这个不是一个合法的左值?因为&操作符的结果是一个右值,它不能当作左值使用。当表达式&ch进行求值时,它的结构应该存储于计算机的什么地方呢?它肯定会位于某个地方,但你无法知道它位于何处。这个表达式并未标识任何机器内存的特定位置,所以它不是一个合法的左值。

    2、cp 可以作为左值,也可以作为右值

    3、&cp 可以作为右值,但是不可以作为左值。

    4、*cp 可以作为右值,也可以作为左值。

    5、*cp + 1 可以作为右值,但是不可以作为左值。*的优先级高于+,所以首先执行间接访问操作,我们可以得到它的值。我们取得这个值的一份拷贝并把它与1相加,表达式的最终结果为字符'b'。这个表达式的最终结果的存储位置并未清晰定义,所以它不是一个合法的左值。优先级表格证实+的结构不能作为左值。

    6、*(cp +  1) 可以作为右值,也可以作为左值。

    7、++cp 可以作为右值,但不可以作为左值。 在这个表达式中,我们增加了指针变量cp的值。表达式的结果是增值后的指针的一份拷贝,因为前缀++先增加它的操作数的值再返回这个结果。这份拷贝的存储位置并未清晰定义,所以它不是一个合法的左值。
 
   8、cp++ 可以作为右值,但是不可以作为左值。 后缀++操作符同样增加cp的值,但它先返回cp值的一份考本然后再增加cp的值。这样,这个表达式的值就是cp原来的值的一份拷贝。
  
   9、* ++cp 可以作为右值,也可以作为左值。

   10、*cp++ 可以作为右值,也可以作为左值。

   11、++*cp 可以作为右值,但是不可以作为左值。 由于这两个操作符的结合性都是从右向左,所以首先执行的是间接访问操作,然后,cp所指向的位置的值增加1,表达式的结构是这个增值后的值的一份拷贝。

   12、(*cp) ++ 可以作为右值,但是不可以作为左值。

   13、++*++cp 可以作为右值,但是不可以作为左值。

  评论这张
 
阅读(1199)| 评论(0)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018