我看到一行 C 看起来像这样:
!ErrorHasOccured() ??!??! HandleError();
它编译正确,似乎运行正常。似乎它正在检查是否发生了错误,如果发生了,它会处理它。但我不确定它实际上在做什么或它是如何做的。看起来程序员确实在尝试表达他们对错误的感受。
我以前从未在任何编程语言中见过 ??!??!
,而且我在任何地方都找不到它的文档。 (Google 对 ??!??!
之类的搜索字词没有帮助)。它有什么作用以及代码示例如何工作?
??!
是转换为 |
的 trigraph。所以它说:
!ErrorHasOccured() || HandleError();
由于短路,这相当于:
if (ErrorHasOccured())
HandleError();
Guru of the Week(与 C++ 相关,但在此处相关),我在哪里选择了这个。
Possible origin of trigraphs 或者正如@DwB 在评论中指出的那样,这更有可能是因为 EBCDIC 很困难(再次)。 This IBM developerworks 板上的讨论似乎支持该理论。
来自 ISO/IEC 9899:1999 §5.2.1.1,脚注 12 (h/t @Random832):
trigraph 序列支持输入未在 ISO/IEC 646 中描述的不变代码集中定义的字符,它是七位美国 ASCII 代码集的子集。
好吧,为什么这通常存在可能与您的示例中存在的原因不同。
这一切都始于半个世纪前,将硬拷贝通信终端重新用作计算机用户界面。在最初的 Unix 和 C 时代,那是 ASR-33 Teletype。
这个设备很慢(10 cps),嘈杂和丑陋,它对 ASCII 字符集的视图以 0x5f 结尾,所以它(仔细看图片)没有任何键:
{ | } ~
The trigraphs 被定义为解决特定问题。这个想法是 C 程序可以使用在 ASR-33 上找到的 ASCII 子集,并且在其他环境中缺少高 ASCII 值。
你的例子实际上是两个??!,每个意思是|,所以结果是||。
但是,几乎按照定义编写 C 代码的人都拥有现代设备,1 所以我的猜测是:有人在炫耀或自娱自乐,在代码中留下一种复活节彩蛋让你找到。
它确实有效,它导致了一个广受欢迎的 SO 问题。
https://i.stack.imgur.com/WbaCR.jpg
ASR-33 电传打字机
1. 就此而言,三元组是由 ANSI 委员会发明的,该委员会在 C 语言取得巨大成功后首次会面,因此原始 C 代码或编码人员都不会使用它们。
#
被替换为 £
。在其他地区,也许“ASCII”没有大括号等。
if (x || y) { a[i] = '\0'; }
在错误的字符集中看起来像 if (x öö y) ä aÄiÅ = 'Ö0'; å
的可能性。
这是一个 C trigraph。 ??!
是 |
,所以 ??!??!
是运算符 ||
<iso646.h>
头文件。
如前所述,??!??!
本质上是两个 trigraphs(又是 ??!
和 ??!
)混合在一起,被预处理器替换翻译成 ||
,即 logical OR .
下表包含每个三元组应该有助于消除替代三元组组合的歧义:
Trigraph Replaces
??( [
??) ]
??< {
??> }
??/ \
??' ^
??= #
??! |
??- ~
来源:C: A Reference Manual 5th Edition
因此,看起来像 ??(??)
的三元组最终将映射到 []
,??(??)??(??)
将被 [][]
替换,依此类推,你明白了。
由于在预处理过程中替换了三元组,您可以使用 cpp
来自己查看输出,使用一个愚蠢的 trigr.c
程序:
void main(){ const char *s = "??!??!"; }
并使用以下方法处理它:
cpp -trigraphs trigr.c
你会得到一个控制台输出
void main(){ const char *s = "||"; }
如您所见,必须指定选项 -trigraphs
否则 cpp
将发出警告;这表明 三元组已成为过去,除了使可能碰到它们的人感到困惑之外,没有任何现代价值。
至于引入三元组背后的基本原理,在查看 the history section of ISO/IEC 646 时可以更好地理解:
ISO/IEC 646 及其前身 ASCII (ANSI X3.4) 在很大程度上认可了电信行业中有关字符编码的现有做法。由于 ASCII 没有提供除英语以外的语言所需的许多字符,因此制作了一些国家变体,用所需的字符替换了一些较少使用的字符。
(强调我的)
因此,从本质上讲,某些国家变体中替换了一些需要的字符(存在三合符的字符)。这导致使用由其他变体仍然具有的字符组成的三元组的替代表示。
char *date = "??-??-??!"
之类的占位符可能不会产生您所期望的(这实际上会产生 char *date = "~~|";
)
if(data??(x??)??(y??)=='??/r' ??!??! data??(x??)??(y??)==0) ??< break; ??>
?:
以增加可读性
ErrorHasOccurred() && HandleError();
也就是说,如果您习惯于 shell 脚本。 :)