??!??! 是啥意思?运算符在 C 中做啥?
Posted
技术标签:
【中文标题】??!??! 是啥意思?运算符在 C 中做啥?【英文标题】:What does the ??!??! operator do in C???!??! 是什么意思?运算符在 C 中做什么? 【发布时间】:2011-12-11 02:56:28 【问题描述】:我看到一行 C 看起来像这样:
!ErrorHasOccured() ??!??! HandleError();
它编译正确,似乎运行正常。似乎它正在检查是否发生了错误,如果发生了,它会处理它。但我不确定它实际上在做什么或它是如何做的。看起来程序员确实在尝试表达他们对错误的感受。
我以前从未在任何编程语言中见过??!??!
,而且我在任何地方都找不到它的文档。 (Google 不提供像 ??!??!
这样的搜索字词)。它有什么作用以及代码示例如何工作?
【问题讨论】:
可悲的是这个程序的宝石won't work in C++17 和更新。 【参考方案1】:??!
是一个trigraph,它转换为|
。所以它说:
!ErrorHasOccured() || HandleError();
由于短路,相当于:
if (ErrorHasOccured())
HandleError();
Possible origin of trigraphs 或@DwB 在 cmets 中指出,这更有可能是因为 EBCDIC 很困难(再次)。 This IBM developerworks 板上的讨论似乎支持该理论。
来自 ISO/IEC 9899:1999 §5.2.1.1,脚注 12 (h/t @Random832):
三字符序列允许输入未在不变代码集中定义为的字符 在 ISO/IEC 646 中描述,它是七位美国 ASCII 代码集的子集。
【讨论】:
原本需要三元组以防您的键盘没有例如“|”象征。这里要么是程序员故意烦人,要么是一些奇怪的编辑器“功能” 不一定是 EBCDIC - 需要三元组的字符集几乎完全匹配 ISO-646 中不变的字符集(即旧的“国家 ascii”标准)。 一个完全可读的替代方案是ErrorHasOccurred() && HandleError();
,也就是说,如果你习惯于 shell 脚本。 :)
请注意,许多编码标准明确禁止使用三合字母和二合字母,许多编译器和静态分析器会标记它们的使用。
自 C++17 起无效:|【参考方案2】:
这是一个 C trigraph。 ??!
是|
,所以??!??!
是运算符||
【讨论】:
trigraph 来自一些键盘没有他们现在拥有的所有键的时期。当某些文本编辑器为特殊事物保留特殊字符时,它也会有所帮助。它主要是过去的遗物和测验的推动者;) 因为有些键盘显然没有“|”所以有些人别无选择,只能反复用头撞键盘,直到出现一个三元组,为他们提供所需的符号。 还有<iso646.h>
这个头文件。【参考方案3】:
嗯,为什么这一般存在可能与您的示例中存在的原因不同。
这一切都始于半个世纪前,将硬拷贝通信终端重新用作计算机用户界面。在最初的 Unix 和 C 时代,那是 ASR-33 Teletype。
这个设备很慢(10 cps),嘈杂和丑陋,它的 ASCII 字符集视图以 0x5f 结尾,所以它(仔细看图片)没有任何键:
| ~
The trigraphs 被定义为解决特定问题。这个想法是 C 程序可以使用在 ASR-33 上找到的 ASCII 子集,以及在其他缺少高 ASCII 值的环境中。
你的例子其实是两个
??!
,每个意思是|
,所以结果是||
。
但是,几乎按照定义,编写 C 代码的人都拥有现代设备,1 所以我的猜测是:有人炫耀或自娱自乐,代码供您查找。
它确实有效,它导致了一个广受欢迎的 SO 问题。
ASR-33 电传打字机
1。就此而言,三元组是由 ANSI 委员会发明的,它在 C 取得巨大成功之后第一次遇到,因此原始 C 代码或编码人员都不会使用它们。
【讨论】:
这不是键盘和字符集中缺少字符的唯一情况。很多 30 多岁及以上的人可能更熟悉 Commodore 64 - 显示的字符集都缺少大括号(可能还有横杠和波浪号) - 在这种情况下,因为“ASCII”不是 ASCII .在 ECMA-6(几乎总是称为 ASCII,但不是 US-ASCII)中,有 18 个区域特定的代码,但我不知道它们是哪些代码。我可以肯定地说的一件事 - 在英国的“ASCII”中,#
被替换为 £
。在其他地区,也许“ASCII”没有大括号等。
Atari 8 位计算机的类似 ATASCII 字符集也缺少 以及 ~ 和 `。
参见 these two ***文章。我差不多老了,还记得 7 位国家字符集的时代(尽管我确信它们仍然在一些黑暗的未扫过的角落里徘徊),而且我第一次学习 C 的那本书发现有必要警告一下if (x || y) a[i] = '\0';
在错误的字符集中看起来像 if (x öö y) ä aÄiÅ = 'Ö0'; å
的可能性。
另一个有趣的历史记录是 Unix(它是 C 所依赖的大平台)可能是第一个具有任何意义的系统(也许是第一个整体),将默认字母值改为小写而不是比大写。虽然我没有亲眼见过很多当代系统,但我认为这是一个真正成熟的标志。除了作为真正唯一体面的操作系统之外,Unix 还将您的大写字母转换为小写字母,而不是反之亦然。那些家伙真的很酷。
我得告诉你一个有趣的故事……IBM RS/6000 工作站的 XL Fortran 编译器是从 XL C 编译器开发的。在最初的几个版本中,它们不小心留在了三元组处理中,因此有一些合法的 Fortran 字符序列(在文字字符串中,IIRC)被误解为 C 三元组,导致一些有趣的错误!【参考方案4】:
如前所述,??!??!
本质上是两个 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 = "~~|";
)以上是关于??!??! 是啥意思?运算符在 C 中做啥?的主要内容,如果未能解决你的问题,请参考以下文章