1.3 词法分析中的贪心法

C语言中的一些符号,比如/*=,都只有一个字符的长度。其他符号比如/*==和标识符,都是几个字符长。当C编译器遇到了一个跟在/后面的*,它必须能决定是把这两个符号各看作一个token,还是把两个一起看作一个token。C使用一个非常简单的规则来解决这个问题:永远取可以得到的最长的token。这就是说,每次从左往右读取,得到一个可能得到的最长的token。这种策略有时也被叫做贪心算法,或者更通俗地说,叫每口吃最大策略。Kernighan和Richie这样描述这种策略:“输入流不断被分割为token,假设当前字符前的输入流都被分割为token了,那么从当前字符开始,下一个token将是当前字符及之后的字符所可能组成的最长的token”。token(除了字符串和字符常量)从不会包含空格(包括tab符和换行符)。

因此,==是一个token,= =是两个token,而a---ba -- - b一样,而不是和a - -- b一样。

类似地,如果一个/是一个token的第一个字符,而/后面紧跟着一个*,那么这两个符号将代表一段注释的开始,而不是代表任何其他含义。

下面的语句看起来是把y赋值为x除以p指向的值所得的商:

  1. y = x/*p /* p points at the divisor */;

实际上,/开始了一段注释,所以编译器只会分析到/之前,换句话说,这个语句只是把y赋值为x,对于p根本都看不到。把这个语句改写为:

  1. y = x / *p /* p points at the divisor */;

或者甚至改写成:

  1. y = x/(*p) /* p points at the divisor */;

都会正确地运行注释中所说的除法。

这种近乎二义性的写法在其他地方也会造成问题。例如,曾经有一段时间,C用=+来表示和+=同样的意思。一些C编译器仍然会允许这种过时的用法。比如编译器会把a=-1;当作a =- 1;后者和a = a - 1;是一样的。这会让本打算表达a = -1;的程序员感觉不可思议。 这种古老的编译器还会把a=/*b;当作a =/ * b ;尽管/*看起来像是一个注释开始符。

这种古老的编译器还会把复合赋值当作两个token。这类编译器对于a >> = 1;不会报错,但是一个严格的ANSI C编译器会拒绝这种写法。