什么??!???!运算符在C中做什么?

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

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

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

我以前从未在任何编程语言中见过??!??!,而且我在任何地方都找不到它的留档。(谷歌对??!??!这样的搜索词没有帮助)。它有什么作用?代码示例是如何工作的?

381068 次浏览

??!三角图,转换为|。所以它说:

!ErrorHasOccured() || HandleError();

其中,由于短路,相当于:

if (ErrorHasOccured())HandleError();

本周的大师(处理C++但与此相关),我在那里拿起这个。

三元组的可能起源或者正如@DwB在评论中指出的那样,更有可能是因为EBCDIC(再次)很困难。这个在IBMDeveloperworks板上的讨论似乎支持这一理论。

来自ISO/IEC 9899:1999§5.2.1.1,脚注12(h/t@刘德华):

三元组序列允许输入未在不变代码集中定义为在ISO/IEC 646中描述,它是七位美国ASCII代码集的子集。

这是一个C三角图??!|,所以??!??!是运算符||

那么,为什么一般存在可能与为什么它存在于您的示例中不同。

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

这个设备很慢(10 cps),嘈杂而丑陋,它对ASCII字符集的看法以0x5f结束,所以它没有(仔细看图片)任何键:

{ | } ~

定义三位一体是为了解决一个特定的问题。这个想法是C程序可以使用在ASR-33上找到的ASCII子集,以及在其他缺少高ASCII值的环境中。

您的示例实际上是??!中的两个,每个都表示|,因此结果是||

然而,几乎根据定义编写C代码的人拥有现代设备,1所以我的猜测是:炫耀或自娱自乐的人,在代码中留下一种复活节彩蛋供您查找。

它确实奏效了,它导致了一个广受欢迎的SO问题。

ASR-33电传打字机

ASR-33电传打字机


1.就此而言,三元组是由ANSI委员会发明的,该委员会首先遇到了之后 C并取得了巨大的成功,因此原始的C代码或编码人员都不会使用它们。

如前所述,??!??!本质上是两个三元组??!??!)混合在一起,由预处理器替换翻译为||,即逻辑或

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

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

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

因此,看起来像??(??)的三图最终将映射到[]??(??)??(??)将被[][]替换,依此类推,你明白了。

由于三元组在预处理过程中被替换,您可以使用#0自己获得输出视图,使用愚蠢的trigr.c程序:

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码不提供英语以外语言所需的大量字符,因此出现了许多国家变体,用需要的字符替换了一些较少使用的字符。

(强调我的)

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