ChatGPT解决这个技术问题 Extra ChatGPT

??!??! 是什么意思?运算符在 C 中做什么?

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

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

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

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

可悲的是这个程序 won't work in C++17 和更新的宝石。
当然,这只是乐趣的一半,除非符号分别命名为 wtfroflmao
我要用这个来惹恼人类

L
Lucas

??! 是转换为 |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 起无效:|
D
DigitalRoss

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

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


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

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


trigraph 来自一些键盘没有他们现在拥有的所有键的时期。当某些文本编辑器为特殊事物保留特殊字符时,它也会有所帮助。它主要是过去的遗物和测验的推动者;)
因为有些键盘显然没有“|”所以有些人别无选择,只能反复用头撞键盘,直到出现一个三元组,为他们提供所需的符号。
然后是 <iso646.h> 头文件。
C
Community

如前所述,??!??! 本质上是两个 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) ??< break; ??>
@wojtow 不,你只是硬编码不够:) 只需添加一些 ?: 以增加可读性