青衣 发表于 2021-7-6 13:53:06

《C陷阱与缺陷》学习笔记(4):词法中的那些陷阱

    道阻且长,行则将至。埋头苦干,不鸣则已,一鸣惊人!加油,骚年!
1 参考资料    书本参考资料

[*]《C陷阱与缺陷》第 1 章,P8 - P12 ;
2 “贪心法”    每一个符号应该包含尽可能多的字符。
    编译器将程序分解成符号的方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分:如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。
    这个处理策略有时被称为 “贪心法” ,或者,更口语化一点,称为 “大嘴法” 。

2.1 代码示例1
    除了字符串与字符常量,符号的中间不能嵌有空白(空格符、制表符和换行符)。
    例如,“==” 是单个符号,“= =” 则是两个符号;
    看下边的表达式:
a---b   // 表达式1
a -- - b// 表达式2
a - -- b// 表达式3
    表达式 1 与表达式 2 含义相同;但是表达式 1 与表达式 3 含义明显不同!

2.2 代码示例2
    下面语句的本意似乎是用 x 除以 p 所指向的值,把所得的商再赋给 y :
y = x/*p      /* p指向除数 */
    很显然,这个语句实际表达的意思只是将 x 的值赋给 y ,后边的全被当做注释了。
  备注:
    此时利用编辑器自带的语法高亮显示,也可以看到 “/*p” 是被当做注释了,字体与注释字体一样。
    比较好的做法是将上述代码修改为如下两种格式:
// 格式一
y = x / *p      /* p指向除数 */

// 格式二
y = x/(*p)      /* p指向除数 */
    这样得到的实际效果才是语句注释所表示的原意。

2.3 代码示例三
    老版本的 C 语言中允许使用 =+ 来代表现在 += 的含义。此时下边这几种表达式的 含义是一样的
a=-1;

a =- 1;

a = a - 1;
    此时如果我们想表达的含义是 “a = -1;” ,那结果就会大不相同。
    还有一种特殊的情况,在老版本编译器中,表现与现在的编译器也不相同,如下几种表达方式也是一样的
// 表示方式一
a=/*b;

// 表示方式二
a =/ *b;
3 整型常量    如果一个整型常量的第一个字符是数字 0 ,那么该常量将被视作八进制数。因此,10 与 010 的含义截然不同。
    需要特别注意一种情况:有时候在上下文中为了格式对齐的需要,可能无意中将十进制数写成了八进制数。例如:
struct
{
    int part_number;
    char *description;
}

parttab[] = {
    046, "left-handed widget"       ,
    047, "right-handed widget"      ,
    125, "frammis"
};
    注意上边的情况,明显把本来想表达的十进制数( 46 、47 ),写成了八进制数。
    后续在使用时,一定要多加注意!
4 字符与字符串    C 语言中的 单引号和双引号 含义迥异,在某些情况下如果把两者弄混,编译器并不会检测报错,从而在运行时产生难以预料的结果。
    用单引号引起的一个字符实际上代表一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。
    因此,对于采用 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 = '/';
    上述代码在编译时,将会报错。因为 ‘/’ 并不是一个字符指针。
    某些 C 编译器对函数参数并不进行类型检查,特比是对 printf 函数的参数。
    因此如果使用【 printf(’\n’); 】来代替正确的【 printf("\n"); 】,则会在程序运行的时候产生难以预料的错误,而不会给出编译器诊断信息。
  译注:现在的编译器一般能够检测到在函数调用时混用单引号和双引号的情形。
5 总结
[*]简单了解了 “贪心法” ;编译器会从左到右一个字符一个字符读入,组合。
[*]在编码过程中,一定不要使用一些似是而非的语句。当对优先级不确定时,最好使用括号;等有时间时,还是详细了解一下优先级顺序会比较有帮助。
[*]注意整型常量十进制与八进制混用的陷阱,避免入坑。
[*]注意单引号与双引号的陷阱。

  

  
文档来源:51CTO技术博客https://blog.51cto.com/u_13727606/2988456
页: [1]
查看完整版本: 《C陷阱与缺陷》学习笔记(4):词法中的那些陷阱