C++ C11&;C++;11扩展和通用字符转义 上下文

C++ C11&;C++;11扩展和通用字符转义 上下文,c++,c,c++11,language-lawyer,c11,C++,C,C++11,Language Lawyer,C11,C11和C++11都支持源文件中的扩展字符,以及通用字符名(UCS),它允许用户仅使用不在基本源字符集中的字符输入字符 C++11还定义了编译的几个翻译阶段。特别是,在翻译的第一个阶段,扩展字符被标准化为UCS,如下所述: §C++11 2.2p1.1: 物理源文件字符在 实现定义方式,以基本源字符集 (为行尾指示器引入新行字符)如果 必要的。已接受物理源文件字符集 实现定义。Trigraph序列(2.4)替换为 对应的单字符内部表示。任何来源 替换不在基本源字符集(2.3)中的文件字符 通过指

C11和C++11都支持源文件中的扩展字符,以及通用字符名(UCS),它允许用户仅使用不在基本源字符集中的字符输入字符

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

§C++11 2.2p1.1:

物理源文件字符在 实现定义方式,以基本源字符集 (为行尾指示器引入新行字符)如果 必要的。已接受物理源文件字符集 实现定义。Trigraph序列(2.4)替换为 对应的单字符内部表示。任何来源 替换不在基本源字符集(2.3)中的文件字符 通过指定该字符的通用字符名。(一) 实现可以使用任何内部编码,只要实际 源文件中遇到扩展字符,并且相同 扩展字符,在源文件中表示为 通用字符名(即使用\uxxx表示法)是 以同等方式处理,除非此替换在 原始字符串文字。)


问题: 因此,我的问题是:

是否有符合标准的程序编译

#include <stdio.h>

int main(void){
        printf("\é\n");
        printf("\\u00e9\n");
        return 0;
}
或编译和打印

é
é
\u00e9
\u00e9
,何时运行


知情的个人意见 我认为答案是它成功编译和打印了
\u00e9
,因为根据上文§2.2p1.1,我们已经

实现可以使用任何内部编码,只要源文件中遇到实际扩展字符,并且源文件中表示为通用字符名的相同扩展字符(即,使用\uxxx表示法),被等价地处理,除非此替换在原始字符串文本中还原,并且我们不在原始字符串文本中

接下来就是

  • 在第1阶段,
    printf(“\n”)映射到
    printf(\\u00e9\n”)
  • 在阶段3中,源文件被分解为预处理令牌(§2.2p1.3),其中字符串literal
    “\\u00e9\n”
    就是其中之一
  • 在阶段5中,字符文字或字符串文字中的每个源字符集成员,以及字符文字或非原始字符串文字中的每个转义序列和通用字符名,都被转换为执行字符集的相应成员(§2.2p1.5)。因此,根据最大芒克原理,
    \\
    映射到
    \
    ,片段
    u00e9
    不被识别为UCN,因此按原样打印
实验 不幸的是,现存的编译器不同意我的观点。我已经用GCC 4.8.2和Clang 3.5进行了测试,下面是他们给我的:

  • 通用条款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
    
  • 叮当声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
    
我使用
hextump-C ucn.cpp
é
字符进行了双重和三重检查,以确保字符显示为
C3 A9
,与预期的UTF-8编码一致。此外,我还验证了一个普通的
printf(“é\n”)
printf(“\u00e9\n”)
可以完美地工作,所以这不是被测试的编译器无法读取UTF-8源文件的问题


谁是对的?

你似乎很困惑,认为
\\u00e9
是一个UCN——它不是。UCS都以
\u
开头,在您的情况下,您有一个提取反斜杠,该反斜杠转义此初始反斜杠。因此,
\\u00e9
是6个字符的序列:
\
u
0
0
e
9

编辑

在第1阶段,printf(“\e\n”);映射到printf(“\u00e9\n”)


这就是问题所在——第1阶段将输入字符转换为源字符,因此
printf(“\e\n”)映射到
p
r
i
n
t
f
,与
p
r
i
n
t
f
)相同,但这与
printf(“\\u00e9\n”)不同映射到,因为后者中有双反斜杠。由于双反斜杠的特殊处理方式,在源代码中无法将反斜杠后跟UCN。

但是,请注意,
“\\u00e9”
不是前面有反斜杠的UCN,并且不可能在后面有反斜杠的字符串或字符文本中写入任何基本源字符序列。因此,
“\ee”
“\\u00e9”
的行为不需要相同:可以很好地定义
“\\u00e9”
的行为,而
“\ee”
的行为是未定义的

如果我们假设一些语法允许反斜杠转义UCN,比如说
“\«\u00e9»”
,那么这将具有未定义的行为,如
“\e”


  • 在第1阶段,
    printf(“\n”)映射到
    printf(\\u00e9\n”)
é
转换为UCN的第一阶段无法创建非UCN,例如
“\\u
$ 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
#include <stdio.h>

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