??!??!とは何ですか 演算子はCで行いますか?


1990

次のようなCの行が表示されました。

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

正しくコンパイルされ、正常に実行されているようです。エラーが発生したかどうかをチェックし、エラーが発生した場合は処理するようです。しかし、私はそれが実際に何をしているのか、どのようにそれをしているのか本当にわかりません。プログラマーがエラーについての感情を表現しようとしているように見えます。

これ??!??!までにどのプログラミング言語でも見たことがなく、そのためのドキュメントをどこにも見つけることができません。(Googleはのような検索用語をサポートしていません??!??!)。それは何をし、コードサンプルはどのように機能しますか?


44
@PeterOlson、どのよう!ErrorHasOccurred() ??!???! HandleError();にコンパイルすると思いますか?です??! ??? !。ポイントを証明しますか?
CVn、2011年

31
クリーンなコードを読むことをお勧めします。ErrorHasOccured()をErrorHasNotOccured()にリファクタリングして、感嘆符をクリーンアップする必要があります...これらの演算子をすべて理解する時間のある人は誰ですか?
KadekM 2015

17
私はむしろErrorHasOccured() && HandleError()自分を好む。それもLuaのやり方です。
Hugo Zink 2015年

76
@KadekM、否定を関数名に移動しても、コードがクリーンになるわけではなく、逆になります。
marcelm 2016

14
検索エンジンでの死闘の後、ここにたどり着いた人への注記SymbolHoundはシンボリック検索に役立ちます。
ヤコブ

回答:


1579

??!はに変換される3 文字表記|です。だからそれは言う:

!ErrorHasOccured() || HandleError();

これは、短絡のため、以下と同等です。

if (ErrorHasOccured())
    HandleError();

今週の第一人者(C ++での取引ですが、ここでは関連があります)。

トリグラフの発生源の可能性、または@DwBがコメントで指摘しているように、EBCDICが(再び)困難であるために可能性が高くなります。IBM developerworksボードでのこの議論は、その理論をサポートしているようです。

ISO / IEC 9899:1999§5.2.1.1から、脚注12(h / t @ Random832):

3文字シーケンスは、7ビットUS ASCIIコードセットのサブセットであるISO / IEC 646で説明されているように、不変コードセットで定義されていない文字の入力を可能にします。


378
トリグラフは、キーボードに「|」などがない場合に必要でした。シンボル。ここでは、プログラマーが故意に迷惑をかけているか、奇妙なエディターの「機能」を使用しています
Martin Beckett

36
ええ、それはと同等if (ErrorHasOccured()) HandleError()です。ありがたいことに、perlコードでこのイディオムに遭遇するだけです。
user786653 '19年

22
これは必ずしもEBCDICである必要はありません。3文字表記を必要とする文字のセットは、ISO-646で不変ではない文字のセット(つまり、古い「国のASCII」標準)とほぼ正確に一致します。
Random832 '19年

52
完全に読みやすい代替案は、ErrorHasOccurred() && HandleError();つまり、スクリプトをシェル化することに慣れている場合です。:)
Yam

18
「ErrorHasOcurredがないか、HandleErrorを実行する必要があります」、@ SparkyRobinsonと読みます。
OmarAntolín-Camarena2015

453

まあ、これが一般に存在する理由は、おそらくあなたの例に存在する理由とはおそらく異なります。

それはすべて、半世紀前にハードコピー通信端末をコンピュータユーザーインターフェイスとして転用することから始まりました。初期のUnixとCの時代は、ASR-33テレタイプでした。

このデバイスは低速(10 cps)でノイズが多く見づらく、ASCII文字セットのビューは0x5fで終了したため、(写真をよく見ると)キーがありませんでした。

{ | } ~ 

トリグラフは、特定の問題を修正するために定義されました。Cプログラムは、ASR-33にあるASCIIサブセットを使用でき、他の環境では高いASCII値が欠落している可能性があるという考えでした。

あなたの例は実際にはの2つであり??!、それぞれの意味な|ので、結果は次のようになります||

しかし、ほとんどの定義によるCのコードを書く人は、近代的な設備、持っていた1:私の推測があるので、誰かが披露や人自身を楽しま、あなたが探してコードにイースターエッグのようなものを残します。

それは確かに機能し、非常に人気のあるSOの質問につながりました。

ASR-33テレタイプ

                                            ASR-33テレタイプ


1.さらに言えば、トライグラフはANSI委員会によって発明され、Cが暴走の成功を収めた後に最初に出会っので、元のCコードやコーダーの誰もそれらを使用しなかったでしょう。


18
キーボードと文字セットで文字が欠落しているのはこれだけではありません。Commodore 64は、30代後半以降の多くの人々に馴染みがある可能性があります。表示された文字セットは両方とも中括弧(およびおそらくバーとチルドも)を欠いていました。この場合、「ASCII」がASCIIではなかったためです。 。ECMA-6(ほとんどの場合、ASCIIと呼ばれますが、US-ASCIIとは呼ばれません)では、18の地域固有のコードがありましたが、それらがどのコードであったかはわかりません。確かに言えることは、イギリスの "ASCII"では、#に置き換えられました£。他の地域では、おそらく "ASCII"にブレースなどはありませんでした
Steve314

7
Atari 8ビットコンピューター用の同様のATASCII文字セットにも、〜と `だけでなく{}もありませんでした。
dan04 2011年

42
これら 2つのウィキペディアの記事を参照してください。私はまだ7ビットの国別文字セットの時代を覚えているくらいの年齢です(ただし、暗い未開封のコーナーではまだ残っていると確信しています)。Cから最初に学んだ本は、間違った文字セットのif (x || y) { a[i] = '\0'; }ようif (x öö y) ä aÄiÅ = 'Ö0'; åに見える可能性。
Ilmari Karonen、2011年

9
もう1つの興味深い歴史的メモは、デフォルトのアルファベット値を大文字ではなく小文字に変換するUnix(Cが乗った大きなプラットフォーム)が、最初に重要なシステム(そしておそらく最初のシステム)だった可能性があるということです。私は多くの現代的なシステムを自分の目で見たことがありませんが、これは洗練された本当のしるしだったと思います。UnixはまともなOSであるだけでなく、大文字を小文字に変換するのではなく、その逆の変換も行います。それらの人は本当にクールでした。
DigitalRoss、2011年

16
おもしろい話ですが、IBM RS / 6000ワークステーションのXL FortranコンパイラーはXL Cコンパイラーから開発されました。最初の数回のリリースでは、それらは誤って3文字表記の処理に残ったため、正当なFortran文字シーケンス(リテラル文字列、IIRC)がC 3文字表記として誤って解釈され、いくつかの興味深いバグが発生しました。
Phil Perry 14

166

Cのトリグラフです。??!|??!??!演算子もそうです||


5
トリグラフは、一部のキーボードが現在持っているすべてのキーを備えていなかった時期から来ています。また、一部のテキストエディタが特殊なもののために特殊文字を予約したときにも、それは保留されます。それは主に過去の遺物であり、クイズを可能にします;)
Joel Falcou

5
一部のキーボードには明らかに「|」がないため そのため、必要な記号を与える3文字表記が発生するまで、キーボードを何度も頭を突かなければならない人もいます。
フクロウ

そして、<iso646.h>ヘッダーファイルがあります。
David R Tribble

149

すでに述べたように??!??!、基本的には2つの3 文字表記??!そして??!再び)が結び付けられ、プリプロセッサーによってに置き換えられ||、つまり論理ORになります。

すべての3文字表記を含む次の表は、別の3文字表記の組み合わせを明確にするのに役立ちます。

Trigraph   Replaces

??(        [
??)        ]
??<        {
??>        }
??/        \
??'        ^
??=        #
??!        |
??-        ~

出典:C:A Reference Manual 5th Edition

したがって、次のよう??(??)に見える3文字表記は、最終的ににマッピングされ[]??(??)??(??)に置き換えられ[][]ます。

トリグラフは前処理中に置換さ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は英語以外の言語に必要な数の文字を提供していなかったため、使用頻度の低い文字を必要な文字に置き換える国別のバリエーションがいくつか作成されました

(強調鉱山)

したがって、本質的に、いくつかの必要な文字(3文字表記が存在する文字)は、特定の国の変形で置き換えられました。これは、他のバリアントがまだ持っていた文字で構成される3文字表記を使用した代替表現につながります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.