【发布时间】:2015-07-21 03:28:28
【问题描述】:
上下文
C11 和 C++11 都支持源文件中的扩展字符,以及通用字符名称 (UCN),它允许仅使用基本源字符集中的字符输入不在基本源字符集中的字符。
C++11 还定义了编译的几个翻译阶段。特别是,扩展字符在翻译的第一阶段被规范化为 UCN,如下所述:
§ C++11 2.2p1.1:
物理源文件字符被映射,在一个 实现定义的方式,以基本的源字符集 (为行尾指示符引入换行符)如果 必要的。接受的物理源文件字符集是 实现定义。三字母序列(2.4)被替换为 相应的单字符内部表示。任何来源 不在基本源字符集(2.3)中的文件字符被替换 通过指定该字符的通用字符名称。 (一个 实现可以使用任何内部编码,只要实际 源文件中遇到的扩展字符,和 扩展字符在源文件中表示为 通用字符名称(即,使用 \uXXXX 表示法)是 同等处理,除非此替换在 原始字符串文字。)
问题
因此,我的问题是:
对程序进行符合标准的编译
#include <stdio.h> int main(void){ printf("\é\n"); printf("\\u00e9\n"); return 0; }失败,编译并打印
é é或编译打印
\u00e9 \u00e9,什么时候运行?
知情的个人意见
我认为答案是它成功编译并打印了\u00e9,因为根据上面的§2.2p1.1,我们有
实现可以使用任何内部编码,只要在源文件中遇到实际扩展字符,并且在源文件中表示的相同扩展字符作为通用-character-name(即使用 \uXXXX 表示法)被等效地处理,除非在原始字符串文字中恢复此替换。,我们不在原始字符串中。
接下来就是
- 在第 1 阶段,
printf("\é\n");映射到printf("\\u00e9\n");。 - 在第 3 阶段,源文件被分解为预处理标记(第 2.2p1.3 节),其中 string-literal
"\\u00e9\n"是其中之一。 - 在第 5 阶段,字符文字或字符串文字中的每个源字符集成员,以及字符文字或非原始字符串文字中的每个转义序列和通用字符名称都会被转换到执行字符集的相应成员 (§2.2p1.5)。因此,根据最大咀嚼原则,
\\映射到\,并且片段u00e9不被识别为 UCN,因此按原样打印。
实验
不幸的是,现存的编译器不同意我的观点。我已经使用 GCC 4.8.2 和 Clang 3.5 进行了测试,这是他们给我的:
-
GCC 4.8.2
$ g++ -std=c++11 -Wall -Wextra ucn.cpp -o ucn ucn.cpp: In function 'int main()': ucn.cpp:4:9: warning: unknown escape sequence: '\303' [enabled by default] printf("\é\n"); ^ $ ./ucn é \u00e9 -
Clang 3.5
$ clang++ -std=c++11 -Wall -Wextra ucn.cpp -o ucn ucn.cpp:4:10: warning: unknown escape sequence '\xFFFFFFC3' [-Wunknown-escape-sequence] printf("\é\n"); ^ ucn.cpp:4:12: warning: illegal character encoding in string literal [-Winvalid-source-encoding] printf("\é\n"); ^ 2 warnings generated. $ ./ucn é \u00e9
我使用hexdump -C ucn.cpp 双重和三重检查é 字符是否显示为C3 A9,与预期的UTF-8 编码一致。此外,我还验证了普通的 printf("é\n"); 或 printf("\u00e9\n"); 可以完美运行,因此这不是测试的编译器无法读取 UTF-8 源文件的问题。
谁是对的?
【问题讨论】:
-
N3881 讨论了这个问题,但我不知道委员会采取了任何行动来解决这个问题。
-
@cpplearner 出色的侦探工作;这正是我想要阅读的那种文件。如果 cmets 是最受欢迎的,我会为你的。
标签: c++ c c++11 language-lawyer c11