美文网首页
C语言中的词法陷阱

C语言中的词法陷阱

作者: 王伯卿 | 来源:发表于2018-07-03 08:03 被阅读0次

    该文章为笔记,因此许多内容摘抄自《C陷阱与缺陷》。
    《C陷阱与缺陷》,全书不厚,但是感觉十分有提醒与启迪作用,值得阅读。是打好良好C语言基础的必读手册之一。

    1.1 =不同于==

    C语言使用 = 作为赋值运算,使用 == 作为比较运算。这两者有巨大的差别。当我们在实际使用的时候,往往会出现如下的错误。

      // example 1
      if(x = y)
       break;
    

    我们本意是用x与y来比较从而来控制程序的进退,但是这里将会进入一个死循环。

    当我们误将比较运算写成赋值运算的时候,会出现意想不到的问题。

       //example 2
       while(c = ' ' || c == '\t' || c == '\n')
        c = getc(f);
    

    本例中的循环语句的本意是跳过文件中的空格符、制表符和换行符。
    由于在比较 ' ' 和变量c的时候,误将比较运算符==写成了赋值运算符= 。因为赋值运算符=的优先级低于逻辑运算符 || ,因此实际上是将以下表达式的值赋值给了c。

    ' ' || c == '\t' || c == '\n'
    

    ' ' 永远为真,所以上述表达值的求值永远为1,所以程序将会进入一个四循环中。

    当我们把比较运算符误写成赋值运算符的时候,同样将会出现编译器无法捕捉的错误。

    if((filedesc == open(argv[i], 0)) < 0) 
      error();
    

    本例中,如果open函数执行成功,将会返回0或正数,执行失败,将会返回-1。本例的本意是将open的返回数与filedesc比较,二者比较的结果与0相比判断程序是否错误。暂时不思考filedesc中存储的是什么值,比较运算符==的最后结果只可能是0或-1,因此结果永远都不可能小于0,因此这个error函数,将没有被机会执行。
    这是一个隐藏的错误,编译器不会报任何错误,因为在它看来,这是一个符合规则的合法程序。

    1.2 & 和 | 不同于 && 和 ||

    这里的具体内容,将在稍后补充。

    1.3 词法分析中的“贪心法”

    原则为每个符号应该包含尽可能多的字符。编译器从左到右读取每一个字符,读了一个再读一个判断是否可以构成一个有意义的字符。我们可以看下面这个例子。

    y = x/*p;
    

    大家看颜色可以发现,读到/,再读下一个,发现是,因此编译器认为这个是注释的开头,因此接下里编译器就不断的往下读取,直至出现/为止。
    因此,这里推荐,抓不准的时候,请一定要加上括号。

      y = x/(*p);
    

    然后我们看下比较变态的例子。

    #include <stdio.h>
    
    int main(void){
      int a = 3;
      int b = 5;
      int sum = a+++b;
      printf("%d,%d,%d\n",a,b,sum);
    }
    //4,5,8
    

    最后输出4,5,8。这里做一个词法分析。
    1)读入a,读入下一个
    2)读入+,读入下一个
    3)读入+,两个+,当成自增符号++使用,左结合给a,因此左边为a++
    4)读入+,读入下一个
    5)读入b,前面的+当作加法来算
    因此就是(a++)+b。

    假设我们往a+++b里多加几个+,变成诸如a+++++b,这样编译器会报错。因为a++ 后在读入两个+,编译器认为是自增符号因此左结合给a++,然而a++后不能在自增了,因此程序报错。

    但是如果我们添加空格,比如这样a+++ ++b,这样就可以编译通过了,因为编译器在读取第三个+后,再往后读取一个,发现是空格,所以就把第三个+当作加号来使用,编译就通过了。

    这些题,完全没有意义。建议使用的时候加括号,然后部门中如果出现这么写的人,建议早点把他辞退。

    1.4 整型常量

    这里需要注意,如果一个整型常量的第一个字符是数字0,那么该常量将被视作八进制数。

    1.5 字符与字符串

    用单引号引起的一个字符实际上代表一个一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。
    用双引号引起的字符串,代表的却是一个指向无名数组起始字符的指针。
    这里只简单举一个例子。

    # include <iostream>
    using namespace std;
    
    int main(int argc, char const *argv[])
    {
        char str1 = 'A';
        int str2 = 65;
        cout<<str1<<endl<<(char) str2<<endl;
        return 0;
    }
    

    最后会输出两个A。

    相关文章

      网友评论

          本文标题:C语言中的词法陷阱

          本文链接:https://www.haomeiwen.com/subject/qzxtuftx.html