Was macht das ??! ??! Betreiber in C tun?

1990

Ich sah eine Linie von C, die so aussah:

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

Es wurde korrekt kompiliert und scheint in Ordnung zu sein. Es scheint, als würde geprüft, ob ein Fehler aufgetreten ist, und wenn ja, wird er behandelt. Aber ich bin mir nicht sicher, was es tatsächlich tut oder wie es es tut. Es sieht so aus, als würde der Programmierer versuchen, seine Gefühle über Fehler auszudrücken.

Ich habe das noch nie ??!??!in einer Programmiersprache gesehen und kann nirgendwo eine Dokumentation dafür finden. (Google hilft nicht bei Suchbegriffen wie ??!??!). Was macht es und wie funktioniert das Codebeispiel?

Peter Olson
quelle
44
@PeterOlson, wie erwarten Sie !ErrorHasOccurred() ??!???! HandleError();die Kompilierung? Das ist ??! ??? !. Beweist den Punkt?
Ein CVn
31
Ich schlage vor, Sie lesen sauberen Code. ErrorHasOccured () sollte in ErrorHasNotOccured () umgestaltet werden, um das Ausrufezeichen zu bereinigen ... wer hat Zeit, all diese Operatoren zu verstehen ??!
KadekM
17
Ich bevorzuge ErrorHasOccured() && HandleError()mich lieber . So macht es auch Lua.
Hugo Zink
76
@KadekM, das Verschieben der Negation in den Funktionsnamen führt nicht zu sauberem Code, sondern zum Gegenteil.
Marcelm
14
Ein Hinweis für alle, die nach einem Kampf bis zum Tod mit ihrer Suchmaschine hier gelandet sind : SymbolHound kann bei symbolischen Suchen helfen.
Jakob

Antworten:

1579

??!ist ein Trigraph , der übersetzt |. So heißt es:

!ErrorHasOccured() || HandleError();

Dies entspricht aufgrund eines Kurzschlusses:

if (ErrorHasOccured())
    HandleError();

Guru der Woche (befasst sich mit C ++, aber hier relevant), wo ich das aufgegriffen habe.

Möglicher Ursprung von Trigraphen oder wie @DwB in den Kommentaren hervorhebt, ist es wahrscheinlicher, dass EBCDIC (wieder) schwierig ist. Diese Diskussion im IBM Developerworks Board scheint diese Theorie zu unterstützen.

Aus ISO / IEC 9899: 1999 §5.2.1.1, Fußnote 12 (h / t @ Random832):

Die Trigraphsequenzen ermöglichen die Eingabe von Zeichen, die nicht im invarianten Codesatz definiert sind, wie in ISO / IEC 646 beschrieben, einer Teilmenge des 7-Bit-US-ASCII-Codesatzes.

user786653
quelle
378
Trigraphen wurden ursprünglich benötigt, falls Ihre Tastatur zB kein '|' hatte. Symbol. Hier ist es entweder der Programmierer, der absichtlich nervt, oder ein bizarres Editor-Feature
Martin Beckett,
36
Ja, das entspricht if (ErrorHasOccured()) HandleError(). Zum Glück begegnen Sie dieser Redewendung normalerweise nur im Perl-Code.
user786653
22
Es ist nicht unbedingt EBCDIC - der Zeichensatz, für den Trigraphen erforderlich sind, entspricht fast genau dem Zeichensatz, der in ISO-646 nicht unveränderlich ist (dh den alten nationalen ASCII-Standards).
Random832
52
Eine perfekt lesbare Alternative wäre ErrorHasOccurred() && HandleError();, wenn Sie an Shell-Scripting gewöhnt sind. :)
Yam Marcovic
18
Lesen Sie es als "Entweder ist kein Fehler aufgetreten oder Sie müssen mit dem Fehler umgehen", @SparkyRobinson.
Omar Antolín-Camarena
453

Nun, warum dies im Allgemeinen existiert, ist wahrscheinlich anders als in Ihrem Beispiel.

Alles begann vor einem halben Jahrhundert mit der Umnutzung von Hardcopy-Kommunikationsterminals als Computerbenutzeroberflächen. In der ersten Unix- und C-Ära war dies der ASR-33-Teletyp.

Dieses Gerät war langsam (10 cps) und laut und hässlich, und seine Ansicht des ASCII-Zeichensatzes endete bei 0x5f, sodass es (siehe Bild) keine der Tasten hatte:

{ | } ~ 

Die Trigraphen wurden definiert, um ein bestimmtes Problem zu beheben. Die Idee war, dass C-Programme die ASCII-Teilmenge verwenden können, die auf dem ASR-33 und in anderen Umgebungen gefunden wurde, in denen die hohen ASCII-Werte fehlen.

Ihr Beispiel ist eigentlich zwei von ??!jeder Bedeutung |, also ist das Ergebnis ||.

Leute, die fast per Definition C-Code schreiben, hatten jedoch moderne Geräte 1, also meine Vermutung: Jemand, der sich vorführt oder amüsiert und eine Art Osterei im Code hinterlässt, das Sie finden können.

Es hat sicher funktioniert, es führte zu einer äußerst beliebten SO-Frage.

ASR-33 Teletyp

                                            ASR-33 Teletyp


1. Im Übrigen wurden die Trigraphen vom ANSI-Komitee erfunden, das sich zum ersten Mal traf, nachdem C zu einem außer Kontrolle geratenen Erfolg geworden war, sodass keiner der ursprünglichen C-Codes oder Codierer sie verwendet hätte.

DigitalRoss
quelle
18
Es ist nicht der einzige Fall von fehlenden Zeichen auf der Tastatur und im Zeichensatz. Der Commodore 64 ist wahrscheinlich vielen Leuten in den späten Dreißigern und darüber besser bekannt - den angezeigten Zeichensätzen fehlten beide Klammern (und wahrscheinlich auch der Balken und die Tilde) - in diesem Fall, weil der "ASCII" kein ASCII war . In ECMA-6 (fast immer ASCII genannt, aber nicht US-ASCII) gab es 18 regionenspezifische Codes, aber ich weiß nicht, um welche Codes es sich handelte. Das einzige, was ich mit Sicherheit sagen kann - im britischen "ASCII" #wurde durch ersetzt £. In anderen Regionen hatte "ASCII" vielleicht keine Zahnspange usw.
Steve314
7
Dem ähnlichen ATASCII-Zeichensatz für Atari 8-Bit-Computer fehlten auch {} sowie ~ und `.
Dan04
42
Siehe diese beiden Wikipedia-Artikel. Ich bin gerade alt genug, um mich noch an die Ära der nationalen 7-Bit-Zeichensätze zu erinnern (obwohl ich sicher bin, dass sie immer noch in dunklen, ungepflegten Ecken verweilen), und das Buch, aus dem ich C gelernt habe, fand es notwendig, vor dem zu warnen Möglichkeit if (x || y) { a[i] = '\0'; }, wie if (x öö y) ä aÄiÅ = 'Ö0'; åim falschen Zeichensatz auszusehen.
Ilmari Karonen
9
Ein weiterer interessanter historischer Hinweis ist, dass Unix (das die große Plattform war, auf der C mitfuhr) möglicherweise das erste System von Bedeutung (und möglicherweise das erste insgesamt) war, bei dem alphabetische Werte eher in Kleinbuchstaben als in Großbuchstaben gesetzt wurden. Obwohl ich viele zeitgenössische Systeme nicht mit eigenen Augen gesehen habe, denke ich, dass dies ein echtes Zeichen für Raffinesse war. Unix ist nicht nur das einzig anständige Betriebssystem, sondern hat auch Großbuchstaben in Kleinbuchstaben umgewandelt und nicht umgekehrt. Diese Jungs waren wirklich cool.
DigitalRoss
16
Lustige Geschichte, die ich dir erzählen muss ... Der XL Fortran-Compiler der IBM RS / 6000-Workstation wurde aus dem XL C-Compiler entwickelt. In den ersten Veröffentlichungen wurden sie versehentlich in der Trigraph-Verarbeitung belassen, sodass es einige legitime Fortran-Zeichensequenzen (in einer wörtlichen Zeichenfolge, IIRC) gab, die als C-Trigraphs falsch interpretiert wurden, was zu einigen interessanten Fehlern führte!
Phil Perry
166

Es ist ein C- Trigraph . ??!ist |, so ??!??!ist der Betreiber||

Joel Falcou
quelle
5
Trigraph stammen aus einer Zeit, in der einige Tastaturen nicht alle Tasten hatten, die sie jetzt haben. Es hilft auch, wenn ein Texteditor Sonderzeichen für besondere Dinge reserviert. Es ist meistens ein Relikt der Vergangenheit und ein Quiz-Enabler;)
Joel Falcou
5
Weil einige Tastaturen anscheinend kein "|" Einige Leute haben also keine andere Wahl, als wiederholt auf die Tastatur zu drücken, bis ein Trigraph auftritt, der ihnen die Symbole gibt, die sie benötigen.
Eule
Und dann ist da noch die <iso646.h>Header-Datei.
David R Tribble
149

Wie bereits erwähnt, ??!??!sind im Wesentlichen zwei Trigraphen ( ??!und ??!wieder) zusammengefügt, die vom Präprozessor ersetzt und in ||das logische ODER übersetzt werden .

Die folgende Tabelle mit jedem Trigraph soll helfen, alternative Trigraphkombinationen zu unterscheiden:

Trigraph   Replaces

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

Quelle: C: Ein Referenzhandbuch 5. Ausgabe

Ein Trigraph, der aussieht, ??(??)wird irgendwann zugeordnet [], ??(??)??(??)ersetzt durch [][]und so weiter. Sie haben die Idee.

Da Trigraphen während der Vorverarbeitung ersetzt werden, können Sie cppmit einem albernen trigr.cProgramm selbst einen Überblick über die Ausgabe erhalten :

void main(){ const char *s = "??!??!"; } 

und Verarbeitung mit:

cpp -trigraphs trigr.c 

Sie erhalten eine Konsolenausgabe von

void main(){ const char *s = "||"; }

Wie Sie sehen können, -trigraphsmuss die Option angegeben werden, sonst cppwird eine Warnung ausgegeben. Dies zeigt, wie Trigraphen der Vergangenheit angehören und keinen modernen Wert haben, außer Menschen zu verwirren, die auf sie stoßen könnten .


Die Gründe für die Einführung von Trigraphen sind im Abschnitt zur Geschichte von ISO / IEC 646 besser zu verstehen :

ISO / IEC 646 und sein Vorgänger ASCII (ANSI X3.4) haben die bestehende Praxis in Bezug auf Zeichenkodierungen in der Telekommunikationsbranche weitgehend gebilligt.

Da ASCII keine Anzahl von Zeichen bereitstellte, die für andere Sprachen als Englisch benötigt wurden, wurde eine Reihe von nationalen Varianten erstellt, die einige weniger verwendete Zeichen durch benötigte ersetzten .

(Hervorhebung von mir)

Im Wesentlichen wurden einige benötigte Zeichen (für die ein Trigraph existiert) in bestimmten nationalen Varianten ersetzt. Dies führt zu einer alternativen Darstellung unter Verwendung von Trigraphen, die aus Zeichen bestehen, die andere Varianten noch hatten.

Dimitris Fasarakis Hilliard
quelle