Cosa fa il ??!??! operatore fare in C?


1990

Ho visto una linea di C che assomigliava a questa:

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

Si è compilato correttamente e sembra funzionare bene. Sembra che stia verificando se si è verificato un errore e, in caso affermativo, lo gestisce. Ma non sono davvero sicuro di cosa stia realmente facendo o di come lo stia facendo. Sembra che il programmatore stia cercando di esprimere i propri sentimenti riguardo agli errori.

Non ho mai visto ??!??!prima in nessun linguaggio di programmazione e non riesco a trovare la documentazione da nessuna parte. (Google non aiuta con termini di ricerca come ??!??!). Cosa fa e come funziona l'esempio di codice?


44
@PeterOlson, come ti aspetti !ErrorHasOccurred() ??!???! HandleError();di compilare? Ecco ??! ??? !. Dimostra il punto?
un CVn

31
Ti suggerisco di leggere su un codice pulito. ErrorHasOccured () dovrebbe essere refactored in ErrorHasNotOccured () ripulendo così il punto esclamativo ... chi ha tempo di capire tutti questi operatori ??!
KadekM,

17
Preferisco preferire ErrorHasOccured() && HandleError()me stesso. Questo è anche il modo in cui Lua lo fa.
Hugo Zink,

76
@KadekM, spostando la negazione nel nome della funzione non si ottiene un codice pulito, piuttosto il contrario.
marcelm

14
Una nota per chiunque sia finito qui dopo una rissa mortale con il proprio motore di ricerca: SymbolHound può aiutare nelle ricerche simboliche.
Jakob,

Risposte:


1579

??!è una trigrafia che si traduce in |. Quindi dice:

!ErrorHasOccured() || HandleError();

che, a causa di corto circuito, equivale a:

if (ErrorHasOccured())
    HandleError();

Guru of the Week (tratta C ++ ma rilevante qui), dove l'ho preso.

La possibile origine delle trigrafi o come sottolinea @DwB nei commenti è più probabile a causa del difficile (di nuovo) EBCDIC. Questa discussione sul consiglio di sviluppo di IBM sembra supportare questa teoria.

Da ISO / IEC 9899: 1999 §5.2.1.1, nota 12 (h / t @ Random832):

Le sequenze trigraph consentono l'immissione di caratteri che non sono definiti nel set di codici invariante come descritto in ISO / IEC 646, che è un sottoinsieme del set di codici ASCII USA a sette bit.


378
Inizialmente le trigraph erano necessarie nel caso in cui la tastiera non avesse ad esempio un '|' simbolo. Qui o il programmatore è deliberatamente noioso o qualche bizzarra 'caratteristica' dell'editor
Martin Beckett,

36
Sì, è equivalente a if (ErrorHasOccured()) HandleError(). Per fortuna di solito incontri questo idioma solo nel codice perl.
user786653,

22
Non è necessariamente EBCDIC - l'insieme di caratteri che richiedono trigrafi corrisponde quasi esattamente all'insieme di caratteri che non sono invarianti in ISO-646 (ovvero i vecchi standard "ascii nazionali").
Casuale 832

52
Un'alternativa perfettamente leggibile sarebbe ErrorHasOccurred() && HandleError();Cioè, se sei abituato a shell scripting. :)
Yam Marcovic,

18
Leggilo come "Nessun erroreHasOcurred o devi HandleError", @SparkyRobinson.
Omar Antolín-Camarena,

453

Bene, perché questo esiste in generale è probabilmente diverso dal perché esiste nel tuo esempio.

Tutto è iniziato mezzo secolo fa con la riproposizione di terminali di comunicazione cartacei come interfacce utente di computer. Nell'era iniziale di Unix e C era il Teletype ASR-33.

Questo dispositivo era lento (10 cps) e rumoroso e brutto e la sua vista del set di caratteri ASCII terminava a 0x5f, quindi non aveva (guarda da vicino l'immagine) nessuno dei tasti:

{ | } ~ 

Le trigrafi sono state definite per risolvere un problema specifico. L'idea era che i programmi C potessero usare il sottoinsieme ASCII trovato su ASR-33 e in altri ambienti mancanti dei valori ASCII elevati.

Il tuo esempio è in realtà due ??!, per ogni significato |, quindi il risultato è ||.

Tuttavia, le persone che scrivono il codice C quasi per definizione avevano attrezzature moderne, 1 quindi la mia ipotesi è: qualcuno che si mette in mostra o si diverte, lasciando una specie di uovo di Pasqua nel codice che puoi trovare.

Di sicuro ha funzionato, ha portato a una domanda SO molto popolare.

Teletipo ASR-33

                                            Teletipo ASR-33


1. Del resto, le trigrafi sono state inventate dal comitato ANSI, che si è incontrato per la prima volta dopo che C è diventato un successo in fuga, quindi nessuno dei codici C o codificatori originali li avrebbe usati.


18
Non è l'unico caso di caratteri mancanti, nella tastiera e nel set di caratteri. È probabile che il Commodore 64 sia più familiare a molte persone tra i trenta e gli anni successivi - i set di caratteri visualizzati mancavano entrambi di parentesi graffe (e probabilmente anche la barra e la tilde) - in questo caso perché "ASCII" non era ASCII . In ECMA-6 (quasi sempre chiamato ASCII, ma non US-ASCII) c'erano 18 codici specifici per regione, ma non so quali fossero. L'unica cosa che posso dire con certezza: nella "ASCII" britannica, è #stata sostituita £. In altre regioni, forse "ASCII" non aveva parentesi graffe, ecc.
Steve314

7
Anche il set di caratteri ATASCII simile per i computer Atari a 8 bit mancava {} e ~ e `.
dan04,

42
Vedi questi due articoli di Wikipedia. Sono quasi abbastanza vecchio da ricordare ancora l'era dei set di caratteri nazionali a 7 bit (anche se sono sicuro che si soffermano ancora in alcuni angoli oscuri e non spazzati), e il libro da cui ho appreso per la prima volta da C ha trovato necessario avvertire del possibilità di if (x || y) { a[i] = '\0'; }apparire come if (x öö y) ä aÄiÅ = 'Ö0'; ånel set di caratteri sbagliato.
Ilmari Karonen,

9
Un'altra interessante nota storica è che Unix (che era la grande piattaforma in cui C cavalcava) potrebbe essere stato il primo sistema di qualsiasi significato (e forse il primo in assoluto) a impostare valori alfabetici predefiniti in lettere minuscole anziché maiuscole. Anche se non ho visto con i miei occhi molti sistemi contemporanei, penso che questo sia stato un vero segno di raffinatezza. Oltre ad essere davvero l'unico sistema operativo decente, Unix ha anche convertito il maiuscolo in minuscolo, piuttosto che viceversa. Quei ragazzi erano davvero fantastici.
DigitalRoss,

16
Storia divertente che devo raccontarvi ... il compilatore XL Fortran della workstation IBM RS / 6000 è stato sviluppato dal compilatore XL C. Nelle prime versioni, sono stati accidentalmente abbandonati nell'elaborazione della trigrafia, quindi c'erano alcune sequenze di caratteri Fortran legittime (in una stringa letterale, IIRC) che sono state erroneamente interpretate come trigrafi in C, portando ad alcuni bug interessanti!
Phil Perry,

166

E 'un C trigraph . ??!è |, così ??!??!è l'operatore||


5
la trigraph proviene da un periodo in cui alcune tastiere non avevano tutti i tasti che hanno adesso. Fa anche male quando alcuni editor di testo riservano caratteri speciali per cose speciali. È principalmente una reliquia del passato e un attivatore di
quiz

5
Perché alcune tastiere apparentemente non hanno "|" quindi alcune persone non hanno altra scelta che premere ripetutamente la tastiera fino a quando non si verifica una trigrafia che dia loro i simboli di cui hanno bisogno.
Gufo

E poi c'è il <iso646.h>file header.
David R Tribble,

149

Come già detto ??!??!è essenzialmente due trigrammi ( ??!e ??!nuovamente) mushed insieme che vengono sostituiti-tradotto a ||, cioè la logica OR , dal preprocessore.

La seguente tabella contenente ogni trigraph dovrebbe aiutare a chiarire le combinazioni di trigraph alternative:

Trigraph   Replaces

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

Fonte: C: A Manuale di riferimento 5a edizione

Quindi una trigrafia che sembra ??(??)alla fine verrà mappata [], ??(??)??(??)verrà sostituita da [][]e così via, avrai l'idea.

Dato che le trigrafi vengono sostituite durante la preelaborazione, è possibile utilizzare cppper ottenere una visione dell'output da soli, utilizzando un trigr.cprogramma stupido :

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

ed elaborandolo con:

cpp -trigraphs trigr.c 

Otterrai un output della console di

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

Come puoi notare, l'opzione -trigraphsdeve essere specificata altrimenti cppemetterà un avviso; questo indica come le trigrafi appartengano al passato e non abbiano alcun valore moderno se non confondere le persone che potrebbero imbattersi in esse .


Per quanto riguarda la logica alla base dell'introduzione delle trigrafi, si comprende meglio se si guarda alla sezione della storia di ISO / IEC 646 :

ISO / IEC 646 e il suo predecessore ASCII (ANSI X3.4) hanno ampiamente appoggiato la prassi esistente in materia di codifica dei caratteri nel settore delle telecomunicazioni.

Poiché ASCII non ha fornito un numero di caratteri necessari per lingue diverse dall'inglese, sono state create numerose varianti nazionali che hanno sostituito alcuni caratteri meno utilizzati con quelli necessari .

(enfatizzare il mio)

Quindi, in sostanza, alcuni caratteri necessari (quelli per i quali esiste una trigrafia) sono stati sostituiti in alcune varianti nazionali. Questo porta alla rappresentazione alternativa usando trigrafi costituiti da personaggi che ancora esistevano altre varianti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.