【问题标题】:Restrictions to Unicode escape sequences in C11C11 中对 Unicode 转义序列的限制
【发布时间】:2020-07-06 16:02:47
【问题描述】:

为什么在 C11 中对 Unicode 转义序列(\unnnn\Unnnnnnnn)有限制,只能表示基本字符集之外的字符?例如,以下代码会导致编译器错误:\u000A is not a valid universal character。 (一些 Unicode“字典”网站甚至将这种无效格式作为 C/C++ 语言的规范,尽管不可否认,这些格式很可能是自动生成的):

static inline int test_unicode_single() {
        return strlen(u8"\u000A") > 1;
}

虽然我知道支持这些基本字符并不是完全必要的,但它们不支持是否有技术原因?就像不能以多种方式表示同一个角色?

【问题讨论】:

  • @EugeneSh。我不明白这如何适用于我的问题。该问题使用 0x000A (U+000A) (LF - Line Feed) 作为示例 Unicode 字符,以 4 个十六进制数字表示。
  • 这个怎么样:port70.net/~nsz/c/c11/n1570.html#6.4.3p2 通用字符名不能指定短标识符小于 00A0 的字符,而不是 0024 ($)、0040 (@) 或 0060 (' ),也不在 D800 到 DFFF 范围内。
  • @EugeneSh。这很有用,尤其是脚注。但问题仍然存在 - 为什么?是否存在安全问题?
  • 你可能想看看stackoverflow.com/a/20158506

标签: c unicode escaping string-literals unicode-escapes


【解决方案1】:

正是为了避免替代拼写。

将通用字符名称 (UCN) 添加到 C 和 C++ 的主要动机是:

  • 允许标识符包含基本源字符集之外的字母(例如ñ)。

  • 允许编写包含基本源字符集之外的字符的字符串和字符文字的可移植机制。

此外,人们希望对现有编译器的更改尽可能有限,特别是编译器(和其他工具)可以继续使用其已建立的(通常是高度优化的)词法分析函数。

这是一个挑战,因为不同编译器的词法分析架构存在巨大差异。在不深入所有细节的情况下,似乎有两种广泛的实施策略是可能的:

  1. 编译器可以在内部使用一些单一的通用编码,例如 UTF-8。其他编码的所有输入文件都将在输入管道的早期被转录成这种内部编码。此外,UCN(无论它们出现在哪里)都将被转换为相应的内部编码。后一种转换可以与续行处理并行进行,续行处理也需要检测反斜杠,从而避免对每个输入字符进行额外的测试,因为这种情况很少会被证明是真的。

  2. 编译器可以在内部使用严格的(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";

但这在查看原始代码时可能并不明显。

【讨论】:

    猜你喜欢
    • 2021-01-30
    • 2018-01-22
    • 1970-01-01
    • 2018-05-28
    • 2011-07-24
    • 2011-05-29
    • 2018-10-31
    • 2020-07-09
    • 1970-01-01
    相关资源
    最近更新 更多