c陷阱与缺陷学习笔记-第一章-词法陷阱

对于c语言来说,程序中的单个字符孤立来看并没有什么意义。
符号(token)值得是程序的一个基本组成单元,其作用相当于一个句子中的单词。从某在意义上来说,一个单词无论出现在那个句子,它代表的意思都是一样的,是一个表义的基本单元。与此类似,符号就是程序中的一个基本信息单元。而组成的字符序列就不同,同一组字符序列在某个上下文环境中属于同一个符号,而在另一个上下文黄金中可能属于完全不用的另一个符号。

例如:p->s="->",如上面的字符‘-’和字符’>'组成的字符序列,在不同的上下文环境中,一个代表运算符,一个代表字符串

编译器复制将程序分解为一个一个符号的部分,称为“词法分析器”

1.1 =不同于==

1.

if(x=y)
   break;

上面的程序中程序员的本意是作比较运算,但是却无意间携程了赋值运算。但是实际上是将y的值付给了x,然后检查该值是否为0.

2.

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

这个例子的本意是跳过文件中的空格符、制表符和换行符.
但是其实是将表达式的值赋给了c,因为’ '不等于0,所以无论c此前是什么值,上述表达式求值的结果都是1,因此循环将一直进行下去直到整个文件介绍。

某些编译器在发现形如e1=e2的表达式出现在循环语句的条件判断语句的时候。其实我们应该避免这种写法,而是显式的进行比较
不建议这么写

if(x=y)
    foo();

而是

if((x=y)!=0)
    foo();

3.
如果把赋值运算符写成比较运算的时候,同样也会造成混淆

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

上面这段代码的本意是将函数open的返回值存储在变量filedesc之中,如何通过判断变量是否小于0来检查函数open是否执行成功。
但是=写成了==
所以实际操作变成了比较函数open的返回值和变量filedesc,如何检查比较结果是否小于0.因为比较的结果只能为0或者1,永远不可能小于0,所以error()将没有机会被调用

1.2

&和|不同于&&和||
&和|是按位运算符
&和&&是逻辑运算符

1.3 词法分析中的贪心法

就是每个符号应该包含尽可能多的字符
编译器将程序分解为符号的方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能就继续读入下一个字符,知道读入的字符已经不再可能组成一个有意义的符号

需要注意的是,除了字符串与字符常量,符号的中间不能嵌有空白(空格符、制表符和换行符),例如==是单个符号,而= =是两个符号

y=x/*p   /*p指向除数*/

但是实际上,/*被编译器理解为一段注释的开始,编译器将不断地读入字符,直到" */"出现为止
所以我们应该写成

y=x/ *p;
或者
y=x/(*p)

一些老版编译器会将a=-1理解为a=a-1.
但是其实程序员的愿意是a=-1

1.4 整型常量

如果一个整型常量的第一个字符是数字0,那么该常量将被视为八进制数。所以10与010的含义截然不同

1.5 字符与字符串

c语言中的单引号和双引号的含义不用
单引号引起的一个字符实际是代表一个证书,整数值对应于该字符在编译器中采用的字符集中的序列值。因此对于采用ASC字符集的编译器而言,‘a’的含义与0141(八进制)或者97(十进制)严格一直
双引号引起的字符串,代表的趋势一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制为0的字符’\0’初始化
下面这个语句

printf("Hello world\n")

char hello[]={'H','e','l','l','o',' ','w','o','r','l','d','\n',0};
printf(hello);

是等效的

例如char *slash='/';在编译的时候会生成一条错误,因为’/'并不是一个字符指针
如果用printf('\n')代替正确的printf("\n")就会在程序运行的时候产生难以预料的错误

猜你喜欢

转载自blog.csdn.net/weixin_42082138/article/details/86542236