某些标点符号,在一些键盘上是无法输入的,所以C提供了这样的替换方式。其中双字符记号有六个,也成为双字符组(digraph):
<: [
:> ]
<% {
%> }
%: #
%:%: ##
如果上述任何字符组出现在字符常量或字符串字面值中,则不会被解释成双字符组;除此之外,它们一律被解释成双字符组,作用和相应的单一字符记号完全一样。比方说,下列的两个程序代码片段完全等价的,并且会产生相同的执行结果。
有双字符组的程序版本:
printf( "Hello,world!My age is <%d>./n", arr<:1:> );
没有双字符组的程序版本:
int arr[ ] = { 10, 20, 30 };printf( "Hello,world!My age is <%d>./n", arr[1] );
输出:Hello,world!My age is <3>. ※假设arr[1]被赋值为3,:)
同样的,最初同事E遇到的问题,实际上是C语言中的三字符组(trigraph),也就是三个字符的表示方式。这样的字符有9个, 它们全都是以两个问号开始,由第三个字符决定三字符组所表示的标点符号是什么.
利用三字符组,实际上我们只需要使用ISO/IEC 646 所定义的字符,就可以写出任何C 程序(ISO/IEC646 是1991 年发布的7 位ASCII 标准)
下面是维基百科中关于ISO/IEC 646的解释:
ISO/IEC 646是国际标准化组织(ISO)和国际电工委员会(IEC)制订的标准,在1972年制订。它是一个 7-位元字符的字集,来自数个国家标准,最主要来自美国的 ASCII(美国信息互换标准代码)。ISO 646 除了英语字母和数字的部分,为所有国家相同外,有些使用字母的国家,可按照实际需要,把 ISO 646 作出修改,以定出该国的字符标准。亦因为当年 8-位元字符集并未得到普遍的接纳,各国把不同的字母或符号放进它们的字符集,以致部分出现在 ASCII 的字母或符号,并没有出现在某些国家的 ISO 646 变体之中。
由于发生在编译的第一个阶段,编译器的预处理器会将三字符组取代成单一对等字符,也就是说会被翻译成等价的字符,不管它们出现在何处,甚至出现在字符常量、字符串字面值、注释,或者预处理指令中,也一样会被取代,这和双字符组的做法不一样。
最初的问题,其实之所以有这样的输出,是因为C/C++编译器在编译的第一个阶段,也就是预处理阶段所做的处理。
但是有很多时候我们并不希望被解释为三字符组,这个时候我们可以把问号表示成转义符(escape sequence):
printf("Exit this application/?/?/?(y/n)");
这样就不会被解释为三字符组了。另外要注意的是,如果用的是Gcc编译器,默认的是不对双字符组(digraph)和三字符组(trigraph)进行预编译转换处理的。
#: ??=
/: ??/
^: ??’
[: ??(
]: ??)
{: ??<
}: ??>
|: ??!
~: ??-
于是程序就这样了: