C11中Unicode转义序列的限制

C11中Unicode转义序列的限制,c,unicode,escaping,string-literals,unicode-escapes,C,Unicode,Escaping,String Literals,Unicode Escapes,为什么C11中对Unicode转义序列(\unnn和\unnnnn)有限制,只有基本字符集之外的字符可以表示?例如,以下代码导致编译器错误:\u000A不是有效的通用字符。(一些Unicode“dictionary”网站甚至将这种无效格式作为C/C++语言的标准格式,尽管无可否认,这些格式可能是自动生成的): static inline int test\u unicode\u single(){ 返回strlen(u8“\u000A”)>1; } 虽然我知道这些基本字符并不一定要被支持,但有

为什么C11中对Unicode转义序列(
\unnn
\unnnnn
)有限制,只有基本字符集之外的字符可以表示?例如,以下代码导致编译器错误:
\u000A不是有效的通用字符
。(一些Unicode“dictionary”网站甚至将这种无效格式作为C/C++语言的标准格式,尽管无可否认,这些格式可能是自动生成的):

static inline int test\u unicode\u single(){
返回strlen(u8“\u000A”)>1;
}

虽然我知道这些基本字符并不一定要被支持,但有没有一个技术上的原因使它们不能被支持呢?比如不能以多种方式表示同一个字符?

这恰恰是为了避免使用其他拼写

< >对C和C++添加通用字符名称(UCNS)的主要动机是:

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

  • 允许使用可移植机制写入字符串和字符文字,其中包括基本源字符集之外的字符

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

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

  • 编译器可以在内部使用某种通用编码,如UTF-8。其他编码中的所有输入文件都将在输入管道的早期转录到该内部编码中。此外,UCS(无论出现在何处)将转换为相应的内部编码。后一种转换可以与延拓行处理并行进行,延拓行处理也需要检测反斜杠,从而避免对每个输入字符进行额外的测试,因为这种情况很少被证明是真的

  • 编译器可以在内部使用严格的(7位)ASCII。允许其他字符的编码中的输入文件将被转录成ASCII,在进行任何其他词法分析之前,非ASCII字符将被转换成UCS

  • 事实上,这两种策略都将在第一阶段(或同等阶段)实施,这远远早于词汇分析。但请注意区别:策略1将UCS转换为内部字符编码,而策略2将不可表示的字符转换为UCS

    这两种策略的共同点是,一旦转录完成,直接输入源流的字符(无论源文件使用何种编码)和用UCN描述的字符之间不再有任何区别。因此,如果编译器允许UTF-8源文件,您可以输入一个
    ñ
    ,作为两个字节0xc3、0xb1或六字符序列
    \u00D1
    ,它们最终都将作为相同的字节序列。这反过来意味着每个标识符只有一个拼写,因此不需要(例如)更改符号表查找

    通常,编译器只是通过编译管道传递变量名,让汇编器或链接器最终处理它们。如果这些下游工具不接受扩展字符编码或UCS(取决于实现策略),则需要对包含此类字符的名称进行“篡改”(转录),以使其可接受。但即使这是必要的,这只是一个小小的改变,可以在一个定义良好的接口上完成

    <>而不是解决产品(或开发团队)在两种策略之间有明确偏好的编译器厂商之间的争论,C和C++标准委员会选择了机制和限制,使两者兼容。特别是,两个委员会都禁止使用表示基本源字符集中已有编码的字符的UCS。这避免了以下问题:

    • 如果我将
      \u0022
      放入字符串文本中,会发生什么情况:

        const char* quote = "\u0022";
      
      如果编译器将UCS转换为它们所表示的字符,那么当词法分析器看到该行时,
      “\u0022”
      将已转换为
      ”““
      ,这是一个词法错误。另一方面,将UCS保留到最后的编译器很乐意将其作为字符串文本接受。禁止使用表示引号的UCN可以避免这种可能的不可移植性

    • 类似地,
      '\u005cn'
      是否为换行符?同样,如果UCN在第1阶段转换为反斜杠,那么在第3阶段,字符串文字肯定会被视为换行符。但是,如果UCN仅在字符文字标记被标识为字符值之后才转换为字符值,则生成的字符文字将包含两个字符(实现定义的值)

    • 那么
      2\u002b2
      呢?即使UCS不应该用于标点符号,这看起来会像是一种添加吗?或者它看起来像一个以非字母代码开头的标识符

    等等,针对大量类似问题

    所有这些细节都可以通过要求UCS不能用于拼写基本源字符集中的字符来避免。这就是标准中体现的内容

    请注意,“基本源字符集”并不包含所有ASCII字符。它不包含大多数控制字符,也不包含doe
    const char* s = "\\
    n";
    
    const char* s = "\n";