首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C11 & C++11扩展和通用字符转义

C11 & C++11扩展和通用字符转义
EN

Stack Overflow用户
提问于 2015-05-10 16:38:38
回答 2查看 914关注 0票数 8

上下文

C11和C++11都支持源文件中的扩展字符,以及通用字符名称(UCN),这允许用户只使用以下字符输入基本源字符集以外的字符。

C++11还定义了编译的几个翻译阶段。特别是,扩展字符在翻译的第一阶段就被归一化为UCNs,如下所述:

§C++11 2.2p1.1:

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

问题

因此,我的问题是:

对程序进行符合标准的编译吗 #包括 int main(void){ printf("\é\n");printf("\u00e9\n");返回0;} 失败、编译和打印 埃埃 或编译和打印 U00e9 \u00e9 什么时候跑?

知情的个人意见

我的论点是,答案是它成功地编译和打印了\u00e9,因为在上面的2.2p1.1中,我们有

实现可以使用任何内部编码,只要在源文件equivalently __中遇到实际的扩展字符,并且在源文件中表示为通用字符名的扩展字符(即使用\uXXXX符号__的)的扩展字符被处理,除非这种替换在原始字符串文本中被恢复,而且我们不是在原始字符串文本中。

然后这就说明了

  • 在第一阶段,printf("\é\n");映射到printf("\\u00e9\n");
  • 在第三阶段中,源文件被分解为预处理令牌(§2.2p1.3),其中字符串-文字"\\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函数'int ()‘:ucn.cpp:4:9:警告:未知转义序列:'\303’默认启用printf(“\\n”);^ ./ucné\u00e9
  • Clang 3.5 $ clang++ -std=c++11 -Wall -Wextra ucn.cpp -o ucn ucn.cpp:4:10:警告:未知转义序列‘\xFFFFFFFFFFFC3’-未知-转义-序列printf("\é\n");^ ucn.cpp:4:12:警告:字符串文字中的非法字符编码-无效源-编码printf(“\\n”);^2生成警告。$ ./ucnéu00e9

我已经进行了双重和-triple检查,é字符使用hexdump -C ucn.cpp显示为C3 A9,这与预期的UTF-8编码一致。此外,我还验证了普通的printf("é\n");printf("\u00e9\n");工作得完美无缺,所以这并不是被测试的编译器无法读取UTF-8源文件的问题。

谁说得对?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-05-10 21:13:09

'é‘不是字符串文字中反斜杠转义的有效字符,因此反斜杠后面跟着'é’作为文字源字符或UCN应该会产生编译器诊断和未定义的行为。

但是,请注意,"\\u00e9"不是以反斜杠开头的UCN,也不可能在字符串或字符文字中写入任何基本源字符序列,后者是一个反斜杠,后面跟着一个UCN。因此,"\é""\\u00e9"不需要有相同的行为:"\\u00e9"的行为可以很好地定义,而"\é"的行为是未定义的。

如果我们假设一些语法允许反斜杠转义一个UCN,比如"\«\u00e9»",那么就会有像"\é"这样的未定义的行为。

  • 在第一阶段,printf("\é\n");映射到printf("\\u00e9\n");

é转换为UCN的第一阶段不能创建非UCN,例如"\\u00e9"

编译器是正确的,但是不要专门用完美的诊断消息来处理这种情况。理想情况下,你得到的是:

代码语言:javascript
复制
$ clang++ -std=c++11  -Wall -Wextra ucn.cpp -o ucn
ucn.cpp:4:10: warning: unknown escape sequence '\é' [-Wunknown-escape-sequence]
        printf("\é\n");
                ^
1 warnings generated.
$ ./ucn
é
\u00e9

这两个编译器都指定,在存在未知转义序列的情况下,它们的行为是将转义序列替换为转义字符,因此"\é"将被视为"é",程序总体上应解释为:

代码语言:javascript
复制
#include <stdio.h>

int main(void){
        printf("é\n");
        printf("\\u00e9\n");
        return 0;
}

这两个编译器都碰巧得到了这种行为,部分是偶然的,但也部分是因为这样对待未识别的转义序列的策略是一个明智的选择:尽管他们只将未识别的转义序列视为反斜杠,后面是字节0xC3,但它们移除反斜杠并将0xC3保留在适当的位置,这意味着UTF-8序列完好无损地留给以后处理。

票数 2
EN

Stack Overflow用户

发布于 2015-05-10 20:10:48

你似乎很困惑,认为\\u00e9是一个UCN --它不是。UCNs都以\u开头,在您的示例中,您有一个提取反斜杠,该反斜杠转义这个初始反斜杠。因此,\\u00e9是由\u00e9六个字符组成的序列。

编辑

在第一阶段,printf("\é\n");映射到printf(“u00e9\n”);

这就是您出错的地方--第一阶段将输入字符转换为源代码,因此printf("\é\n");映射到p r i n t f ( " \ é \ \d25 " >d27d28<p>D29D30d30d31d32/code>d35代码>代码代码<代码<代码><代码<<代码><代码><代码>代码><代码>代码><代码><代码><代码><代码>但这与printf("\\u00e9\n");映射到的不一样,因为后者使用了双反斜杠。由于对双反斜杠的特殊处理,在源中不可能有反斜杠后跟UCN。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30153902

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档