包含不可表示字符的三字符的字符文字的含义

在使用ASCII作为字符集的C编译器中,字符文字'??<'将等于'{' ,即0x7B。 在字符集没有{字符?的编译器上,该文字的值是多少?

在字符串文字之外,编译器可以推断??<应该具有与开括号字符相同的含义 ,即使编译器字符集没有开括号字符。 实际上,三字符的整个目的是允许使用可表示字符序列来代替不可表示的字符。 该规范要求三字符甚至在字符串文字中处理,然而,这让我感到困惑。 如果编译器的字符集包含{字符,编译器可以允许'{'表示为'??<' ,但字符集包括{我看不出程序员不会简单地使用它。 如果字符集不包含{ ,但是,这似乎是首先使用三元组的唯一原因,编译器应该用什么可表示的字符替换??< with?

当谈到环境,特别是文件时,C标准有意变得相当模糊。 以下保证有关三字符及其相应字符的编码:

C11(n1570)5.1.1.2 p1(“翻译阶段”)[emph。 矿]

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

因此,必须将三字符序列映射到单个字节。 此单字节字符必须位于与基本字符集中的任何其他字符不同的基本字符集中。 编译器在翻译过程中如何在内部处理它们并不是真正可观察到的行为,因此它无关紧要。

如果写入文本流,它可以被转换(当我读取它时,如果底层编码没有某个特定字符的编码,则可能返回到三字符序列)。 它可以再次读回,如果它被认为是打印字符,必须比较相等。 同上。 7.21.2 p2:

[…]从文本流中读取的数据必须与之前写入该流的数据相等,只有在以下情况下:数据仅包含打印字符,控制字符包含水平制表符和新行; 空格字符前面不会有任何换行符; 最后一个字符是换行符。 […]

同上。 7.4 p3:

术语打印字符是指特定于语言环境的字符集的成员,每个字符占据显示设备上的一个打印位置; 术语控制字符是指不是打印字符的特定于语言环境的字符集的成员。 *)所有字母和数字都是打印字符。

*)在使用7位US ASCII字符集的实现中,打印字符是其值从0x20(空格)到0x7E(代字号)的字符; 控制字符是其值从0(NUL)到0x1F(US)的字符,以及字符0x7F(DEL)。

对于二进制流,同上。 7.21.2 p3:

二进制流是一个有序的字符序列,可以透明地记录内部数据。 在同一实现中,从二进制流读入的数据应与先前写入该流的数据进行比较。 但是,这样的流可以在流的末尾附加实现定义数量的空字符。

在上面的评论中,问题出现了

 printf("int main(void) ??< ??>\n"); // (1) printf("int main(void) ?\?< ?\?>\n"); // (2) 

始终适用于代码生成,并保证该语句的输出可编译。 我找不到要求isprint('??<')等的规范性引用(for (1) )或甚至isprint('<')等(for (2) )返回非零,但是C89的基本原理关于溪流说:

需要在文本流I / O中保留的字符集是编写C程序所需的字符集。 意图是标准应该允许以最便携的方式编写C语言翻译器。 为此目的不需要诸如退格之类的控制字符,因此不强制它们在文本流中的处理。

'??<'等被写入二进制流时,它必须映射到单个字节,如此打印,是唯一的并且可以与任何其他基本字符区分开来,并且当读回时比较等于'??<'


相关: C89关于三字母的理由 。

在字符集没有{字符?的编译器上,该文字的值是多少?

没有这样的(符合)编译器。 {基本源字符集的一部分 (C99中为5.2.1 / 3 ,C ++ 11中为[lex.charset] / 1 )。 基本执行字符集 (程序在运行时使用的内容)应至少包含基本源字符集的所有成员(C99中的相同5.2.1 / 3 ,C ++中的[lex.charset] / 3) 11)。

正如@Mankarse指出的那样,发明三字符不是为了支持缺少某些字符的编译器(同样,没有这样的编译器),而是支持人们键入缺少输入这些字符所必需的键的键盘。