zoukankan      html  css  js  c++  java
  • C语言中“c = a+++b”,这种结构合理吗?

    C语言会同意一些"令人震惊"的结构,下面的结构是合法的吗,我们来看看几个例子。

    c = a+++b;

    以下代码是合法的吗,咋的一看不禁有这样的疑问?

    int a = 5, b = 7, c;

    c = a+++b;

    这个代码确实不咋符合习惯的写法,但是不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?


     

    根据最处理原则,编译器应该能够尽可能处理所有合法的用法。因此,上面的代码会被处理成:

    c = a++ + b;

    我们来测试一下:

    #include <stdio.h>

    int main()

    {

      int a = 5, b = 7, c;

      c = a+++b;

      printf("a = %d,b = %d,c = %d",a,b,c);

      return 0;

    }

    输出结果如下:


     

    其执行顺序:

    b不变,c = a + b;,则c = 5 + 7 = 12

    a++,那么a = 6;

    c = a+++++b;


    如果你学习C/C++遇到瓶颈,迷茫,困惑,那么不妨加入小编的学习企鹅圈子,跟着前辈一起交流学习,永远会比单打独斗强得多!


    上面那么可能只是开胃菜,那么这个表达式呢,有什么想法?咱们二话不说,直接上代码测试。

    #include <stdio.h>

    int main()

    {

      int a = 5, b = 7, c;

      c = a+++++b;

      //c = (a++) + (++b);

      //c = ((a++)++) + b;

      printf("a = %d,b = %d,c = %d",a,b,c);

      return 0;

    }

    编译结果如下:


     

    虽然一看就知道意思是(a++)+(++b);,但是编译就通不过,我们把括号加上,c = (a++) + (++b);,编译通过,那么出错信息大概是:括号影响了优先级?我们从侧面以及原理来解析一下。


     

    侧面解析一下

    对于a+++++b这一段代码,编译系统从左至右扫描整条语句,先遇到a++,判断出来是一个a的后缀自加运算;

    然后接着扫描,遇到一个+,+是一个二目运算符,它的左边已经有一个运算数a++了,系统就向右搜索第二个运算数;

    又遇到一个+,++比+的运算级别要高,这时,编译系统就将两个+看成一个整体来处理;

    既然是++,编译系统就认定,肯定它的左边或右边有一个变量,编译系统先搜索左边,发现++,不是变量;

    再搜索右边,发现+b,+b是什么东西?编译系统是无法搞明白的;

    因此它就认为++是一个缺少左值的自增运算符,于是提示错误给用户:lvalue required as increment operand

    原理解析一下

    C语言在这里遵循词法解析的贪婪匹配原则。优先匹配尽可能多字符的符号,无论是否有语法错误(因为词法分析时还没有语法检查)。

    于是a+++++b会被当作a ++ ++ + b,这是非法的表达式,因此产生编译错误。

    这个问题在ISO C99标准中直接以示例描述,原文如下:

    “EXAMPLE 2 The program fragment x+++++y is parsed as x ++ ++ + y, which violates a constraint on increment operators, even though the parse x ++ + ++ y might yield a correct expression.

    那么,也许是c = ((a++)++) + b;这个意思呢?

    我们编译结果依然报错,如下:


     

    这涉及到“左值”的问题。百度百科定义如下:

    “左值(lvalue) 是B语言/C语言/C++语言等类C语言中的一类表达式。“左”(left)的原意是指可以放在赋值符号“=”的左边,但其实也表示能作为&和++等操作符的操作数(B语言中已经如此)。而且,现代C/C++中的含义已经不局限于此。lvalue的l被重新解释为location。这也对应于ISO C11/ISO C++11的内存位置(memory location)。

    总结:a存在左值,可以有表达式:a++。但是(a++)不存在左值,无法继续执行(a++)++操作,所以最终报错。


     

    怎么样才能编译正确呢?

    a+++++b在编译时会报错,那么a++ + ++b呢?来看以下代码:

    #include <stdio.h>

    int main()

    {

    int a = 5, b = 7, c;

    c = a++ + ++b;

    printf("a = %d,b = %d,c = %d",a,b,c);

    return 0;

    }

    结果如下:


     

    其代码与c = (a++) + (++b);结果一样,说明是正确的,其按照下面顺序执行:

    先执行b自加,b变为8;相当于:b = b+ 1;

    求a与b之和,赋给c;相当于:c = a + b ;//c = 5+8;

    执行第二步之后,a自加1:a++;

    c=(++a,b++,a++,++b);

    这个表达式看着爽不爽?我们知道自增自减运算,表示对自身进行a=a+1或者a=a-1的运算。

    ++a表示在调用前就a+1,a++表示在调用后+1。

    int c=(++a,b++,a++,++b);这个逗号隔开的表示用最后一个式子对C进行赋值,测试如下:

    #include <stdio.h>

    int main()

    {

    int a = 5, b = 7, c;

    c=(++a,b++,a++,++b);

    printf("a = %d,b = %d,c = %d",a,b,c);

    return 0;

    }

    输出的结果如下:


     

    这段执行的顺序如下

    先执行++a,a=6;

    再执行b++,b=8;

    接下来a++,a=7;

    再执行++b,b=9;

    把最后一个的式子b=9的值赋给c,所以c等于9。如果改成c=(++a,b++,a++,b++);,那么c就是等于8,因为是调用后再自增。

    总结

    我们看到这些有趣且不常见的代码的时候,无需怀疑,只需要根据经验一步一步分析,就会发现其中的玄妙之处。在此我总结以下几点。

    如果你一看就知道了答案,或猜出正确答案,说明基础做得好,点个赞。

    如果你很犹豫,不知道答案,这也是正常的,因为很少见到这样写代码的,但是我们也需要去了解,才能究其根源。

    我总结这个问题,是想提起一个关于代码编写风格,代码的可读性,代码的可修改性的话题,这样我们在写代码的时候才能够注意到,更好的编码。


     

    最后,如果你也想成为程序员,想要快速掌握编程,赶紧加入学习企鹅圈子!

    里面有资深专业软件开发工程师,在线解答你的所有疑惑~编程语言入门“so easy”

    编程学习书籍:


     

    编程学习视频:


     
  • 相关阅读:
    003 01 Android 零基础入门 01 Java基础语法 01 Java初识 03 Java程序的执行流程
    002 01 Android 零基础入门 01 Java基础语法 01 Java初识 02 Java简介
    001 01 Android 零基础入门 01 Java基础语法 01 Java初识 01 导学
    001 Android Studio 首次编译执行项目过程中遇到的几个常见问题
    Dora.Interception,为.NET Core度身打造的AOP框架 [2]:以约定的方式定义拦截器
    Dora.Interception,为.NET Core度身打造的AOP框架 [1]:更加简练的编程体验
    监视EntityFramework中的sql流转你需要知道的三种方式Log,SqlServerProfile, EFProfile
    轻量级ORM框架——第二篇:Dapper中的一些复杂操作和inner join应该注意的坑
    轻量级ORM框架——第一篇:Dapper快速学习
    CF888G Xor-MST(异或生成树模板)
  • 原文地址:https://www.cnblogs.com/mu-ge/p/14057445.html
Copyright © 2011-2022 走看看