问:

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

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

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

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

答1:

huntsbot.com高效搞钱,一站式跟进超10+任务平台外包需求

??! 是转换为 | 的 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 代码集的子集。

如果您的键盘没有“|”,则最初需要三元组象征。这里要么是程序员故意惹人厌烦,要么是一些奇怪的编辑器“功能”

它不一定是 EBCDIC - 需要三元组的字符集几乎完全匹配 ISO-646 中不变的字符集(即旧的“国家 ascii”标准)。

一个完全可读的替代方案是 ErrorHasOccurred() && HandleError(); 也就是说,如果您习惯于 shell 脚本。 :)

请注意,许多编码标准明确禁止使用 Trigraphs 和 Digraphs,并且许多编译器和静态分析器会标记它们的使用。

自 C++17 起无效:|

答2:

huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。

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

这一切都始于半个世纪前,将硬拷贝通信终端重新用作计算机用户界面。在最初的 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 代码或编码人员都不会使用它们。

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

这不是键盘和字符集中缺少字符的唯一情况。很多 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 三元组,导致一些有趣的错误!

答3:

huntsbot.com – 程序员副业首选,一站式外包任务、远程工作、创意产品分享订阅平台。

这是一个 C trigraph。 ??! 是 |,所以 ??!??! 是运算符 ||

trigraph 来自一些键盘没有他们现在拥有的所有键的时期。当某些文本编辑器为特殊事物保留特殊字符时,它也会有所帮助。它主要是过去的遗物和测验的推动者;)

因为有些键盘显然没有“|”所以有些人别无选择,只能反复用头撞键盘,直到出现一个三元组,为他们提供所需的符号。

然后是 头文件。

答4:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

如前所述,??!??! 本质上是两个 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 代码将很难阅读:if(data??(x??)??(y??)=='??/r' ??!??! data??(x??)??(y??)==0) ??

@wojtow 不,你只是硬编码不够:) 只需添加一些 ?: 以增加可读性

原文链接:https://www.huntsbot.com/qa/jWl9/what-does-the-operator-do-in-c?lang=zh_CN&from=csdn

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com