??!??!运算符在C中执行?


1989

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

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

它编译正确,似乎可以正常运行。看起来它正在检查是否发生了错误,如果发生,它将进行处理。但是我不太确定它的实际作用或运行方式。看起来程序员似乎正在尝试表达他们对错误的感受。

我以前从未??!??!在任何编程语言中见过它,并且在任何地方都找不到它的文档。(Google无法帮助搜索字词,例如??!??!)。它是做什么的,代码示例如何工作?


44
@PeterOlson,您希望!ErrorHasOccurred() ??!???! HandleError();如何编译?那是??! ??? !。证明这一点?
CVn

31
我建议您阅读干净的代码。应该将ErrorHasOccured()重构为ErrorHasNotOccured(),从而清除感叹号...谁有时间了解所有这些运算符?
KadekM 2015年

17
我宁愿ErrorHasOccured() && HandleError()自己。这也是Lua的方法。
雨果·辛克

76
@KadekM,将否定词移入函数名称并不能产生清晰的代码,反之亦然。
marcelm '16

14
给使用搜索引擎而死的人们的注释SymbolHound可以帮助进行符号搜索。
雅各布

Answers:


1578

??!是翻译成的三部曲|。所以说:

!ErrorHasOccured() || HandleError();

由于短路,其等效于:

if (ErrorHasOccured())
    HandleError();

本周最佳专家(使用C ++进行交易,但在此处相关),在这里我学到了。

三联字母的可能起源或在评论中@DwB指出,这很可能是由于EBCDIC很难(再次)。在IBM developerWorks板的讨论似乎支持这一理论。

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

三字符组序列使能输入未定义在不变代码集中的字符,如ISO / IEC 646所述,它是七位US ASCII代码集的子集。


376
如果您的键盘没有例如“ |”,则最初需要使用三字母组合 符号。在这里,要么是程序员故意在烦人,要么是一些怪异的编辑“功能”
Martin Beckett

35
是的,它等同于if (ErrorHasOccured()) HandleError()。幸运的是,您通常只会在perl代码中遇到这个习惯用法。
2011年

22
不一定是EBCDIC-要求三字母组合的字符集几乎与在ISO-646中不变的字符集(即旧的“国家ascii”标准)完全匹配。
2011年

52
一个完全可读的替代方法ErrorHasOccurred() && HandleError();是:如果您习惯了shell脚本编写。:)
Yam Marcovic

18
将其读为“ @SparkyRobinson”,或者为“没有ErrorHasOcurred,要么必须为HandleError”。
OmarAntolín-Camarena2015年

453

好吧,为什么它通常存在与为什么它存在于您的示例中可能不同。

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

该设备运行缓慢(10 cps),且嘈杂且丑陋,其ASCII字符集的视图以0x5f结尾,因此它(仔细看图片)没有任何按键:

{ | } ~ 

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

您的示例实际上是两个??!,每个都有含义|,所以结果是||

但是,根据定义,编写C代码的人们几乎拥有现代化的设备,1因此,我的猜测是:有人炫耀自己或自娱自乐,在代码中留下了某种复活节彩蛋供您查找。

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

ASR-33电传打字机

                                            ASR-33电传


1.为此,三字母组合是由ANSI委员会发明的, C取得巨大成功之后,他们第一次见面,因此原始的C代码或编码人员都不会使用它们。


18
这不是键盘和字符集中缺少字符的唯一情况。在30年代及以后,许多人可能会对Commodore 64更加熟悉-显示的字符集都缺少花括号(也可能缺少小节和波浪号)-在这种情况下,因为“ ASCII”不是ASCII 。在ECMA-6(几乎总是称为ASCII,但没有称为US-ASCII)中,有18个特定于区域的代码,但我不知道它们是哪个代码。我可以肯定地说的一件事-在英国的“ ASCII”中#被替换为£。在其他地区,也许“ ASCII”没有花括号等
。– Steve314

7
用于Atari 8位计算机的类似ATASCII字符集也缺少{}以及〜和`。
2011年

42
请参阅 两篇 Wikipedia文章。我差不多老了,还记得7位国家字符集的时代(尽管我确信它们仍然徘徊在一些未扫描的黑暗角落),而我最初从中学习C的书发现有必要对if (x || y) { a[i] = '\0'; }看起来像if (x öö y) ä aÄiÅ = 'Ö0'; å在错误的字符集中的可能性。
Ilmari Karonen 2011年

9
另一个有趣的历史记录是Unix(这是C引入的大型平台)可能是第一个将默认字母值设置为小写而不是大写的系统(也许是第一个整体)。尽管我没有亲眼目睹许多当代系统,但我认为这是成熟的真正标志。除了确实是唯一一个不错的操作系统,Unix还将大写字母转换为小写字母,反之亦然。那些家伙真的很棒。
DigitalRoss

16
我要告诉你一个有趣的故事... IBM RS / 6000工作站的XL Fortran编译器是从XL C编译器开发的。在前几个发行版中,它们不小心留在了三字符组处理中,因此有一些合法的Fortran字符序列(在文字字符串IIRC中)被误解为C三字符组,从而导致一些有趣的错误!
Phil Perry 2014年

166

这是C 三部曲??!|??!??!操作员也是如此||


5
Trigraph来自某个键盘没有现在拥有的所有键的时期。当某些文本编辑器为特殊内容保留特殊字符时,也会发生这种情况。它主要是过去的遗物和
测验使能

5
因为某些键盘显然没有“ |” 因此,有些人别无选择,只能反复敲击键盘,直到出现三边形,为他们提供所需的符号。
猫头鹰

然后是<iso646.h>头文件。
David R Tribble

149

如前所述??!??!实际上是两个三合??!??!再次)捣成泥在一起会被替换,翻译成||,即逻辑或,由预处理器。

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

Trigraph   Replaces

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

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

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

由于在预处理过程中替换了三字母组合,您可以使用cpp一个愚蠢的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没有提供英语以外的语言所需的许多字符,因此产生了许多国家变体,用一些需要的字符代替了一些较少使用的字符

(强调我的)

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.