正是为了避免替代拼写。
将通用字符名称 (UCN) 添加到 C 和 C++ 的主要动机是:
此外,人们希望对现有编译器的更改尽可能有限,特别是编译器(和其他工具)可以继续使用其已建立的(通常是高度优化的)词法分析函数。
这是一个挑战,因为不同编译器的词法分析架构存在巨大差异。在不深入所有细节的情况下,似乎有两种广泛的实施策略是可能的:
-
编译器可以在内部使用一些单一的通用编码,例如 UTF-8。其他编码的所有输入文件都将在输入管道的早期被转录成这种内部编码。此外,UCN(无论它们出现在哪里)都将被转换为相应的内部编码。后一种转换可以与续行处理并行进行,续行处理也需要检测反斜杠,从而避免对每个输入字符进行额外的测试,因为这种情况很少会被证明是真的。
-
编译器可以在内部使用严格的(7 位)ASCII。编码允许其他字符的输入文件将被转录为 ASCII,在任何其他词法分析之前将非 ASCII 字符转换为 UCN。
实际上,这两种策略都将在第 1 阶段(或等效)中实施,该阶段早于词法分析发生。但请注意区别:策略 1 将 UCN 转换为内部字符编码,而策略 2 将不可表示的字符转换为 UCN。
这两种策略的共同点是,一旦转录完成,直接输入到源流中的字符(以源文件使用的任何编码方式)与使用 UCN 描述的字符之间不再有任何区别。因此,如果编译器允许 UTF-8 源文件,您可以输入 ñ 作为两个字节 0xc3、0xb1 或作为六个字符序列 \u00D1,它们最终将作为相同的字节序列。反过来,这意味着每个标识符只有一个拼写,因此不需要(例如)对符号表查找进行更改。
通常,编译器只是通过编译管道传递变量名,最终由汇编器或链接器处理。如果这些下游工具不接受扩展字符编码或 UCN(取决于实施策略),则需要“修改”(转录)包含此类字符的名称以使其可接受。但即使这是必要的,也只是一个小改动,可以在定义明确的界面上完成。
C 和 C++ 标准委员会选择了使这两种策略兼容的机制和限制,而不是解决其产品(或开发团队)对这两种策略有明确偏好的编译器供应商之间的争论。特别是,两个委员会都禁止使用表示在基本源字符集中已经具有编码的字符的 UCN。这样可以避免以下问题:
-
如果我将\u0022 放在字符串文字中会发生什么:
const char* quote = "\u0022";
如果编译器将 UCN 转换为它们所代表的字符,那么当词法分析器看到该行时,"\u0022" 将被转换为 """,这是一个词法错误。另一方面,将 UCN 保留到最后的编译器会欣然接受它作为字符串文字。禁止使用表示引号的 UCN 可以避免这种可能的不可移植性。
-
同样,'\u005cn' 会是换行符吗?同样,如果 UCN 在第 1 阶段转换为反斜杠,那么在第 3 阶段字符串文字肯定会被视为换行符。但是,如果 UCN 仅在字符文字标记被识别后才转换为字符值,则生成的字符文字将包含两个字符(实现定义的值)。
-
那么2 \u002B 2 呢?即使 UCN 不应该用于标点符号,这是否看起来像是一个附加项?还是看起来像一个以非字母代码开头的标识符?
等等,针对大量类似的问题。
所有这些细节都可以通过要求 UCN 不能用于拼写基本源字符集中的字符的简单权宜之计来避免。这就是标准中所体现的内容。
请注意,“基本源字符集”并不包含所有 ASCII 字符。它不包含大多数控制字符,也不包含 ASCII 字符 $、@ 和 `。这些字符(在 C 或 C++ 程序中除了字符串和字符文字之外没有任何意义)可以分别写为 UCN \u0024、\u0040 和 \u0060。
最后,为了了解您需要解开什么样的结才能正确地对 C(或 C++)进行词法分析,请考虑以下 sn-p:
const char* s = "\\
n";
因为续行在第 1 阶段处理,在词法分析之前,并且第 1 阶段只查找由反斜杠后跟换行符组成的两个字符序列,因此该行与
const char* s = "\n";
但这在查看原始代码时可能并不明显。