除了pp-number的实例之外,还有哪些典型的、属于有效预处理记号、但却是无效记号的例子?
除了pp-number的实例(例如 0xe+1),有哪些是有效的预处理标记的典型例子,但它们实际是无效标记?
额外问题:所有标记都是有效的预处理标记吗?(换句话说:是否存在任何有效标记,不能通过“预处理标记 => 标记”转换得到?)
解决方案
根据C 2024 6.4.1,preprocessing-token 是以下之一:
- header-name
- identifier
- pp-number
- character-constant
- string-literal
- punctuator
- 每个不可归入上述任一项的通用字符名称
- 每个不可为上述任一项的非空格字符
任何 header-name 若不匹配 string-literal 的形式,例如 <stdlib.h>,都是一个预处理标记,但不是有效标记。然而,header-name 仅在某些预处理指令和表达式中被识别,并不会保留到翻译阶段7(将预处理标记转换为标记时),因此也不会把 header-name 转换为标记。
一个 identifier 是 token 的一个选项,因此任何作为 identifier 的 preprocessing-token 都是一个有效的 token(尽管其中一些在阶段7 会被转换为 keyword)。
如问题所述,一些 pp-number 预处理标记不是有效标记。
character-constant 是 constant 的一个选项,而 constant 又是 token 的一个选项,且 string-literal 和 punctuator 也是 token 的选项,因此每一个这样的实例都是一个有效的 token。
剩下的最後两项, universaI character names和无法归入上述六种类型之一的非空格字符。6.4.1指出一个 ' 或 " 可能匹配最后一类(因为它们并非数字常量的一部分,如新的 0b1101'1111'0010'0010,并且不能形成字符常量或字符串字面量——要么缺少结束符,要么在字符常量或字符串字面量的语法上有无效之处),因此它们会成为一个预处理标记但不是有效标记的实例。
在我看来,这两类中的任何预处理标记都不能成为有效的标记。如果universal character name或非空格字符能够成为标识符的一部分,那么我们就会得到一个 identifier 作为 preprocessing-token。因此这个预处理标记不可能成为 identifier 类型的 token。同样,它也不能成为 keyword、string-literal、或 punctuator。在 token 的选项中,剩下的只有 constant。查看这些的文法,它们要么是一个 character-constant(与前述排除方式相同),要么是一个 enumeration-constant(它是一个 identifier,已排除),要么是一个 predefined-constant(false、true、或 nullptr,都不匹配这两类中的任一项),要么是一个 integer-constant 或 floating-constant,而它们都以数字开头。
所以,不能成为有效标记的预处理标记是:
- 任何不符合 string-literal 形式的 header-name,尽管这在将预处理标记转换为标记的过程中从未发生过。
- 任何不符合 integer-constant 或 floating-constant 形式的 pp-constant。
- 任何不能归入前六种类型之一的通用字符名称。
- 任何不能归入前六种类型之一的非空格字符,包括一个单独的
'或"。
关于额外的问题,对于一个 token 的每一个选项,在 preprocessing-token 中都存在一个等于它或比它更广的前身形式。