题 什么是C ??!??!操作员呢?


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

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

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

我从未见过 ??!??! 在使用任何编程语言之前,我无法在任何地方找到它的文档。 (谷歌没有帮助搜索条件,如 ??!??!)。它做了什么以及代码示例如何工作?


1605
2017-10-19 16:56


起源


以其他方式表达你的情绪,不要使用三字母,写出人类可以理解的代码 - David Heffernan
@PeterOlson,你怎么看? !ErrorHasOccurred() ??!???! HandleError(); 编译?那是 ??!  ???  !。证明了这一点? - Michael Kjörling
我建议你阅读清洁代码。 ErrorHasOccured()应该重构为ErrorHasNotOccured(),从而清理感叹号......谁有时间了解所有这些操作符?? - KadekM
我更喜欢 ErrorHasOccured() && HandleError() 我。这也是Lua如何做到的。 - Hugo Zink
@KadekM,将否定转换为函数名称并不能代替干净的代码,而是相反。 - marcelm


答案:


??! 是一个 三字母 转化为 |。所以它说:

!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中描述,它是7位US ASCII码集的子集。


1320
2017-10-19 16:58



如果您的键盘没有例如“|”,则最初需要Trigraphs符号。这是程序员故意烦人或者一些奇怪的编辑'功能' - Martin Beckett
是的,它相当于 if (ErrorHasOccured()) HandleError()。值得庆幸的是,你通常只在perl代码中遇到这个习惯用法。 - user786653
它不一定是EBCDIC - 需要三字符的字符集几乎完全匹配ISO-646中不是不变的字符集(即旧的“国家ascii”标准)。 - Random832
一个完全可读的替代方案是 ErrorHasOccurred() && HandleError(); 也就是说,如果您习惯于编写shell脚本。 :) - Yam Marcovic
读它为“要么没有ErrorHasOcurred,要么你必须HandleError”,@ SparkyRobinson。 - Omar Antolín-Camarena


那么,为什么这一般存在可能与你的例子中存在的原因不同。

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

这个设备很慢(10 cps),噪音和丑陋,它的ASCII字符集视图以0x5f结束,因此它(仔细观察图片)没有任何键:

{ | } ~ 

三卦 被定义为修复特定问题。我们的想法是,C程序可以使用ASR-33上的ASCII子集以及缺少高ASCII值的其他环境。

你的例子实际上是两个 ??!,每个意思 |,结果是 ||

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

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

ASR-33 Teletype

ASR-33电传打字机


就此而言,三角帆是由ANSI委员会发明的,它首先遇到了  C成为失败的成功,因此原始的C代码或编码器都不会使用它们。


355
2017-10-19 21:09



这不是键盘和字符集中唯一缺少字符的情况。对于三十多岁及以上的很多人来说,Commodore 64可能更熟悉 - 显示的字符集都缺少括号(也可能是条形和波形) - 在这种情况下,因为“ASCII”不是ASCII 。在ECMA-6(几乎总是称为ASCII,但不是US-ASCII)中,有18个区域特定的代码,但我不知道它们是哪些代码。我可以肯定的一件事 - 在英国的“ASCII”中, #被替换为 £。在其他地区,也许“ASCII”没有大括号等。 - Steve314
Atari 8位计算机的类似ATASCII字符集也缺少{}以及〜和`。 - dan04
看到 这些  二 维基百科的文章。我已经足够大了,仍然记得7位国家字符集的时代(尽管我确信它们仍然留在一些黑暗的未经扫描的角落里),而我第一次从中学到C的书发现有必要警告它可能性 if (x || y) { a[i] = '\0'; } 看起来像 if (x öö y) ä aÄiÅ = 'Ö0'; å 在错误的charset。 - Ilmari Karonen
另一个有趣的历史记录是,Unix(这是C平台上的大型平台)可能是第一个将默认字母值默认为小写而不是大写的系统(可能是第一个整体)。虽然我没有亲眼看到许多现代系统,但我认为这是一个复杂的真实迹象。除了真正唯一体面的操作系统之外,Unix还将你的大写转换为低级,而不是相反。那些家伙真的很酷。 - DigitalRoss
有趣的故事我得告诉你...... IBM RS / 6000工作站的XL Fortran编译器是从XL C编译器开发的。在前几个版本中,他们意外地离开了三字母处理,因此有一些合法的Fortran字符序列(在字符串中,IIRC)被误解为C三字符,导致一些有趣的错误! - Phil Perry


这是一个C. 三字母??! 是 |所以 ??!??! 是运营商 ||


140
2017-10-19 16:58



为什么有人会用??而不是| ??? / - Fatemeh Karimi
trigraph来自某个键盘没有现在拥有的所有键的时期。当某些文本编辑器为特殊事物保留特殊字符时,它也会有效。它主要是过去的遗物和一个quizz启动器;) - Joel Falcou


如前所述 ??!??! 基本上是两个 三合 (??! 和 ??! 再次)汇集在一起​​被取代 - 翻译成 ||,即 逻辑或,由预处理器。

包含所有三字母的下图将有助于消除替代三字母组合的歧义:

enter image description here (图片取自 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没有提供除英语以外的语言所需的一些字符, 制作了许多国家变体,用一些不太常用的角色替换了所需的角色


80
2018-03-25 02:24



⁺¹用于其他三字母表。 - Hi-Angel