第一章 词法陷阱
1.1 =不同于==
while ( c == ’ ‘ || c == ‘\t’ || c == ‘\n’ )
c=getc(f)
漏写=
while ( c = ’ ‘ || c == ‘\t’ || c == ‘\n’ )
c=getc(f)
由于=的优先级低于||,所以判断表达式等价于如下
' ' || c == ‘\t’ || c == ‘\n’
因为 ' '的ASCII码值为32不等于零,所以无论c取何止,上述表达式求值的结果都是1。
if ( x = y )
foo();
应该写作
if ( ( x = y ) != 0 )
foo();
将赋值符号写成比较运算
if ( ( filedesc == open ( argv[i], 0 ) ) < 0)
error();
比较运算符==的结果只可能是0或1,永远不可能小于0。
1.2 &和|不同于&&和||
1.3 词法分析中的“贪心法”
只有一个字符长,称为单字符符号。包含了多个字符,称为多字符符号。
每一个符号应该包含尽可能多的字符,也就是说,编译器将程序分解成符号的方法是,从左到右一个字符一个字符的读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。这个处理策略有时被称为“贪心法”。
a --- b 与 a -- - b 等价,与a - -- b 含义不同
y = x /*p /* p指向除数 */
/*被编译器理解为一段注释的开始,编译器将不断地读入字符,直到*/出现为止。
将上面的语句重写如下:
y = x / *p /* p指向除数 */
或者更加清楚一点,写作:
y = x / ( *p ) /* p指向除数 */
1. 4 整型常量
如果一个整型常量的第一个字符是数字0,那么该常量将被视作八进制数。因此,10与010的含义截然不同。
此外,许多C编译器会把8和9也作为八进制数字处理。例如,0195的含义是1×82+9×91+5×80,也就是141(十进制)或者0215(八进制)。ANSI C标准也禁止这种用法。
需要注意这种情况,有时候在上下文中为了对齐格式的需要,可能无意中将十进制写成八进制数,例如
struct {
int part_number;
char *description;
}parttab[] = {
046, “ left-handed widget ” ,
047, “ right-handed widget ”,
125, “ frammis “
};
1. 5 字符与字符串
用单引号引起的一个字符实际上代表了一个整数,整数值对应于该字符在编译器中采用的字符集中的序列值。因此,对于采用ASCII字符集的编译器而言,’ a ‘的含义与0141(八进制)或者97(十进制)严格一致。
用双引号引起的字符串,代表的却是一个指向无名数组起始字符的指针,该数组被双引号之间的字符以及一个额外的二进制值为零的字符 ’\0’ 初始化。
printf ( “ Hello world\n “);
等效于
char hello[] = { ‘H’, ‘e’ ,’l’, ‘l’, ‘o’, ‘ ‘, ‘w’, ‘o’, ‘r’, ‘l’, ‘d’, ‘\n’ ,0 };
printf (hello);
char *slash= ‘/’;
在编译时将会生成一条错误消息,因为‘/’并不是一个字符指针。然而,某些编译器对函数参数并不进行类型检查,特别是对printf函数的参数。因此,如果用
printf ( ‘\n’);
来代替正确的
printf ( “ \n“);
则会在程序运行的时候产生难以预料的错误,而不会给出编译器诊断信息。
现在的编译器一般能够检测到函数调用时混用单引号和双引号的情形。
被双括号括起来的字符串中,注释符 /* 属于字符串的一部分,而在注释中出现的双引号 “ “又属于注释的一部分。