4

我试图理解 C11 标准中的通用字符名称,发现 C11 标准的 N1570 草案在翻译阶段 1 和 5 以及 UCN 的形成和处理方面比 C++11 标准的细节要少得多他们。这是每个人都必须说的:

翻译阶段 1

N1570 草案 C11 5.1.1.2p1.1:

如有必要,物理源文件多字节字符以实​​现定义的方式映射到源字符集(为行尾指示符引入换行符)。Trigraph 序列被相应的单字符内部表示替换。

C++11 2.2p1.1:

物理源文件字符以实现定义的方式映射到基本如有必要,源字符集(为行尾指示符引入换行符)。接受的物理源文件字符集是实现定义的。三字母序列 (2.4) 被相应的单字符内部表示代替。任何不在基本源字符集 (2.3) 中的源文件字符都将替换为指定该字符的通用字符名。(实现可以使用任何内部编码,只要在源文件中遇到的实际扩展字符,以及在源文件中表示为通用字符名称的相同扩展字符(即,使用 \uXXXX 表示法)是等效处理,除非此替换在原始字符串文字中恢复。)

翻译阶段 5

N1570 草案 C11 5.1.1.2p1.5:

字符常量和字符串文字中的每个源字符集成员和转义序列都被转换为执行字符集的相应成员;[...]

C++ 2.2p1.5:

字符文字或字符串文字中的每个源字符集成员,以及字符文字或非原始字符串文字中的每个转义序列和通用字符名称,都将转换为执行字符集的相应成员;[...]

(强调差异)

问题

  1. 在 C++11 标准中,非常清楚的是,不在基本源字符集中的源文件字符被转换为 UCN,并且它们在同一位置被完全视为 UCN,唯一的例外是原始字符串。C11也是这样吗?当 C11 编译器看到多字节 UTF-8 字符(例如 )时°,它是否也将其转换为\u00b0阶段 1,并将其视为\u00b0出现在那里?

  2. 换句话说,在哪个翻译阶段结束时(如果有),以下代码片段是否在 C11 中首次转换为文本等价形式?

    const char* hell° = "hell°";
    

    const char* hell\u00b0 = "hell\u00b0";
    
  3. 如果在 2. 中,答案是“没有”,那么在哪个翻译阶段,这两个标识符首先被理解为指代同一事物,尽管在文本上有所不同?

  4. 在 C11 中,字符/字符串文字中的 UCN 是否也在阶段 5 中转换?如果是这样,为什么从标准草案中省略这一点?
  5. 在 C11 和 C++11 中如何处理标识符中的 UCN(与已经提到的字符/字符串文字相反)?他们是否也在第 5 阶段转换?或者这是实现定义的东西?例如,GCC 是否以 UCN 编码形式或实际 UTF-8 打印出此类标识符?
4

2 回答 2

2

评论变成了答案

有趣的问题!

C 标准可以保留更多未说明的转换,因为它们是实现定义的(并且 C 没有原始字符串来混淆问题)。

  1. 它在 C 标准中所说的就足够了——除了它让你的问题 1 无法回答。
  2. 我认为第二季度必须是“第 5 阶段”,但需要注意的是“令牌流是等效的”。
  3. Q3 严格来说是 N/A,但第 7 阶段可能是答案。
  4. Q4 是“是”,它之所以这么说是因为它提到了“转义序列”,而 UCN 是转义序列。
  5. Q5 也是“第 5 阶段”。

第 1 阶段和第 5 阶段中的 C++11 授权流程是否符合 C11 的措辞(撇开原始字符串)?

我认为它们实际上是相同的。差异主要来自 C++ 特有的原始文字问题。一般来说,C 和 C++ 标准尽量不让事情无缘无故地不同,特别是尽量让预处理器的工作和低级字符在两者中解析相同(自从 C99 添加了对 C++//注释的支持以来,这变得更容易了,但是随着向 C++11 添加原始文字,这显然变得更加困难)。

有一天,我将不得不更彻底地研究原始的文字符号及其含义。

于 2013-09-25T00:12:16.943 回答
1

首先,请注意这些区别自 1998 年以来就存在;UCN 最初是在新标准 C++98(ISO/IEC 14882,第 1 版:1998)中引入的,然后进入 C 标准的 C99 修订版;但是 C 委员会(和现有的实现者,以及他们预先存在的实现)并不认为 C++ 方式是实现这一技巧的唯一方法,特别是在极端情况和使用比 Unicode 更小的字符集的情况下,或者只是不同;例如,将映射表从任何支持的编码传送到 Unicode 的要求是 1998 年 C 供应商的当务之急。

  1. C 标准(有意识地)避免决定这一点,并让编译器选择如何进行。虽然您的推理显然是在用于源代码和执行的 UTF-8 字符集的上下文中进行的,但是有大量(并且预先存在的)不同的 C99/C11 编译器可用,它们使用不同的集合;并且委员会认为它不应该在这个问题上过多地限制实施者。根据我的经验,大多数编译器在实践中都保持不同(出于性能原因。)
  2. 由于这种自由度,一些编译器可以在阶段 1 之后保持相同(就像 C++ 编译器一样),而其他编译器可以在阶段 7 之后将其区分为一级字符;在第 5 阶段之后,第二个度数字符(在字符串中)应该是相同的,假设度数字符是实现支持的扩展执行字符集的一部分。

对于其他答案,我不会在乔纳森的答案中添加任何内容。

关于您关于 C++ 更具确定性的过程是否符合标准 C 的附加问题,这显然是一个目标;如果你发现一个极端情况(一个不符合 C99 和 C11 标准的符合 C++11 的预处理器),那么你应该考虑向 WG14 委员会询问潜在的缺陷。

显然,反过来是不正确的:可以编写一个预处理器来处理符合 C99/C11 但不符合 C++ 标准的 UCN;最明显的区别是

#define str(t) #t
#define str_is(x, y)  const char * x = y " is " str(y)
str_is(hell°,      "hell°");
str_is(hell\u00B0, "hell\u00B0");

兼容 C 的预处理器可以以与您的示例类似的方式呈现(并且大多数都这样做),因此将具有不同的呈现;但我的印象是需要一个符合 C++ 标准的预处理器才能转换为(严格等效)

const char* hell°      = "hell°"       " is " "\"hell\\u00b0\"";
const char* hell\u00b0 = "hell\\u00b0" " is " "\"hell\\u00b0\"";

最后但同样重要的是,我相信没有多少编译器完全符合这个细节级别!

于 2013-11-01T16:21:47.953 回答