6.4 词法元素
TIP
这一章讨论的是“源码在词法层面到底会被切成什么”。如果不先把预处理记号、记号、最长匹配这些规则理顺,后面很多语法判断都会产生错觉。
6.4 总则
标准把语言中的最小词法单元分成两套:
- 记号(token):在翻译阶段 7 和 8 中使用;
- 预处理记号(preprocessing token):在翻译阶段 3 到 6 中使用。
记号的类别包括:
- 关键字;
- 标识符;
- 常量;
- 字符串字面量;
- 标点符号。
预处理记号的类别包括:
- 头名称;
- 标识符;
- 预处理数;
- 字符常量;
- 字符串字面量;
- 标点符号;
- 不能归入以上类别的单个通用字符名;
- 不能归入以上类别的单个非空白字符。
每个最终要转换为记号的预处理记号,都必须具有关键字、标识符、常量、字符串字面量或标点符号的词法形式。单独一个通用字符名也必须落入其他某一类预处理记号。
预处理记号之间可以由空白分隔。这里的空白包括注释、空白字符,或两者兼有。某些情况下,翻译阶段 4 中空白的有无本身也会影响语义,而不只是分隔作用。
除头名称内部,以及字符常量或字符串字面量的引号之间外,空白不能出现在一个预处理记号内部。
预处理时遵循最长匹配原则:如果输入流截至某个字符已经成功划分完毕,那么下一个预处理记号应当取“能够构成某个预处理记号的最长字符序列”。
这个规则有一个重要例外:头名称只会在
#include、#embed、__has_include、__has_embed,以及实现定义的某些#pragma位置中被识别为头名称。在这些上下文里,既可看作头名称、也可看作字符串字面量的字符序列,应识别为头名称。
IMPORTANT
这就是为什么 1Ex 会整体被视为一个预处理数,而不会先拆成 1 和 Ex。词法分析先做最长匹配,不能先按“这样拆更像我想要的表达式”来理解。
标准给出两个典型例子:
1Ex会被解析为一个预处理数,即使把它拆成1与Ex之后也许能组成有效表达式;x+++++y会被解析为x ++ ++ + y,从而违反自增运算符约束,而不会被解析为x ++ + ++ y。