我看到一行C,看起来像这样:

!ErrorHasOccured() ??!??! HandleError();

它编译正确,运行正常。它似乎在检查是否发生了错误,如果发生了,它会处理它。但我不太确定它实际上在做什么,或者它是如何做的。看起来程序员确实在表达他们对错误的感觉。

我从未见过??!??!我在任何地方都找不到它的文档。(谷歌对搜索词如????!?!没有帮助)。它做什么以及代码示例如何工作?


当前回答

如前所述??!??!本质上是两个三元组(??!和??!)混合在一起,被替换后被预处理器转换为||,即逻辑OR。

包含每个三元图的下表应有助于消除备选三元图组合的歧义:

Trigraph   Replaces

??(        [
??)        ]
??<        {
??>        }
??/        \
??'        ^
??=        #
??!        |
??-        ~

来源:C:A参考手册第5版

那么一个看起来像的三角图??(??)最终将映射到[]??(??)??(??)将被[][]等替换,你明白了。

由于在预处理过程中替换了trigraphs,所以您可以使用一个愚蠢的trigr.c程序,使用cpp自己获取输出的视图:

void main(){ const char *s = "??!??!"; } 

并用以下方法进行处理:

cpp -trigraphs trigr.c 

您将获得控制台输出

void main(){ const char *s = "||"; }

正如您所注意到的,必须指定选项-trigraphs,否则cpp将发出警告;这表明三角图已成为过去,除了让可能碰到它们的人感到困惑之外,没有任何现代价值。


至于引入三角图背后的基本原理,在查看ISO/IEC 646的历史部分时,可以更好地理解:

ISO/IEC 646及其前身ASCII(ANSI X3.4)在很大程度上认可了电信行业中有关字符编码的现有实践。由于ASCII没有为英语以外的语言提供所需的字符数量,因此产生了一些国家变体,用所需字符替换了一些较少使用的字符。

(强调矿井)

因此,从本质上讲,某些必要的字符(存在三角图的字符)在某些国家变体中被替换。这导致使用由其他变体仍然存在的字符组成的三角图的替代表示。

其他回答

好吧,一般来说,这可能与你的例子中的原因不同。

这一切始于半个世纪前,将硬拷贝通信终端重新用作计算机用户界面。在最初的Unix和C时代,这就是ASR-33 Teletype。

该设备速度慢(10 cps),噪音大,外观难看,ASCII字符集的视图以0x5f结尾,因此(仔细看图片)没有任何键:

{ | } ~ 

三角图的定义是为了解决特定的问题。其想法是C程序可以使用ASR-33和其他缺少高ASCII值的环境中的ASCII子集。

你的例子实际上是??!,每个都表示|,所以结果是|。

然而,根据定义,编写C代码的人几乎都拥有现代化的设备,因此我的猜测是:有人在炫耀或逗乐自己,在代码中留下一种复活节彩蛋,让你找到。

它确实奏效了,这导致了一个非常流行的SO问题。

ASR-33电传打字机为此,三角图是由ANSI委员会发明的,该委员会在C获得巨大成功后首次开会,因此没有一个原始的C代码或代码编写者会使用它们。

这是一个C三角图??!是|,所以??!??!是接线员吗||

如前所述??!??!本质上是两个三元组(??!和??!)混合在一起,被替换后被预处理器转换为||,即逻辑OR。

包含每个三元图的下表应有助于消除备选三元图组合的歧义:

Trigraph   Replaces

??(        [
??)        ]
??<        {
??>        }
??/        \
??'        ^
??=        #
??!        |
??-        ~

来源:C:A参考手册第5版

那么一个看起来像的三角图??(??)最终将映射到[]??(??)??(??)将被[][]等替换,你明白了。

由于在预处理过程中替换了trigraphs,所以您可以使用一个愚蠢的trigr.c程序,使用cpp自己获取输出的视图:

void main(){ const char *s = "??!??!"; } 

并用以下方法进行处理:

cpp -trigraphs trigr.c 

您将获得控制台输出

void main(){ const char *s = "||"; }

正如您所注意到的,必须指定选项-trigraphs,否则cpp将发出警告;这表明三角图已成为过去,除了让可能碰到它们的人感到困惑之外,没有任何现代价值。


至于引入三角图背后的基本原理,在查看ISO/IEC 646的历史部分时,可以更好地理解:

ISO/IEC 646及其前身ASCII(ANSI X3.4)在很大程度上认可了电信行业中有关字符编码的现有实践。由于ASCII没有为英语以外的语言提供所需的字符数量,因此产生了一些国家变体,用所需字符替换了一些较少使用的字符。

(强调矿井)

因此,从本质上讲,某些必要的字符(存在三角图的字符)在某些国家变体中被替换。这导致使用由其他变体仍然存在的字符组成的三角图的替代表示。

??! 是一个转换为|的三元组。所以上面写着:

!ErrorHasOccured() || HandleError();

由于短路,等效于:

if (ErrorHasOccured())
    HandleError();

本周大师(与C++有关,但与此相关),我在这里学到了这一点。

可能是trigraphs的起源,或者正如@DwB在评论中指出的那样,更可能是因为EBCDIC很难(再次)。IBM开发人员委员会上的讨论似乎支持这一理论。

根据ISO/IEC 9899:1999§5.2.1.1,脚注12(h/t@Random832):

三角图序列允许输入未在“不变代码集”中定义为其是七位US ASCII码集的子集。