Que fait le ??!??! opérateur faire en C?

1990

J'ai vu une ligne de C qui ressemblait à ceci:

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

Il a compilé correctement et semble fonctionner correctement. Il semble qu'il vérifie si une erreur s'est produite et si c'est le cas, il la gère. Mais je ne suis pas vraiment sûr de ce qu'il fait réellement ou comment il le fait. Il semble que le programmeur essaie d'exprimer ses sentiments sur les erreurs.

Je n'ai jamais vu le ??!??!précédent dans aucun langage de programmation, et je ne trouve aucune documentation pour cela nulle part. (Google n'aide pas avec les termes de recherche comme ??!??!). Que fait-il et comment fonctionne l'exemple de code?

Peter Olson
la source
44
@PeterOlson, comment comptez-vous !ErrorHasOccurred() ??!???! HandleError();compiler? Voilà ??! ??? !. Prouve le point?
un CVn
31
Je vous suggère de lire le code propre. ErrorHasOccured () devrait être refactorisé en ErrorHasNotOccured (), nettoyant ainsi le point d'exclamation ... qui a le temps de comprendre tous ces opérateurs ??!
KadekM
17
Je ErrorHasOccured() && HandleError()me préfère plutôt . C'est aussi comme ça que Lua le fait.
Hugo Zink
76
@KadekM, déplacer la négation dans le nom de la fonction ne signifie pas un code propre, bien au contraire.
marcelm
14
Une note pour quiconque s'est retrouvé ici après un combat à mort avec son moteur de recherche: SymbolHound peut vous aider dans vos recherches symboliques.
Jakob

Réponses:

1579

??!est un trigraphe qui se traduit par |. Donc ça dit:

!ErrorHasOccured() || HandleError();

qui, en raison des courts-circuits, équivaut à:

if (ErrorHasOccured())
    HandleError();

Gourou de la semaine (traite du C ++ mais pertinent ici), où j'ai repris cela.

Origine possible des trigraphes ou comme le souligne @DwB dans les commentaires, il est plus probable en raison de la difficulté (encore une fois) d'EBCDIC. Cette discussion sur la carte IBM developerworks semble soutenir cette théorie.

D'après ISO / IEC 9899: 1999 §5.2.1.1, note de bas de page 12 (h / t @ Random832):

Les séquences de trigraphes permettent l'entrée de caractères qui ne sont pas définis dans le jeu de codes invariant comme décrit dans ISO / IEC 646, qui est un sous-ensemble du jeu de codes US ASCII à sept bits.

user786653
la source
378
Des trigraphes étaient à l'origine nécessaires au cas où votre clavier n'aurait pas par exemple un '|' symbole. Ici, c'est soit le programmeur qui est délibérément ennuyeux, soit une «fonctionnalité» de l'éditeur bizarre
Martin Beckett
36
Ouais, c'est équivalent à if (ErrorHasOccured()) HandleError(). Heureusement, vous ne rencontrez généralement cet idiome qu'en code perl.
user786653
22
Ce n'est pas nécessairement EBCDIC - l'ensemble des caractères qui nécessitent des trigraphes correspond presque exactement à l'ensemble des caractères qui ne sont pas invariants dans ISO-646 (c'est-à-dire les anciennes normes «nationales ascii»).
Random832
52
Une alternative parfaitement lisible serait, c'est-à ErrorHasOccurred() && HandleError();-dire, si vous êtes habitué au shell scripting. :)
Yam Marcovic
18
Lisez-le comme «Soit aucune erreur n'a eu lieu, soit vous devez gérer l'erreur», @SparkyRobinson.
Omar Antolín-Camarena
453

Eh bien, pourquoi cela existe en général est probablement différent de la raison pour laquelle il existe dans votre exemple.

Tout a commencé il y a un demi-siècle avec la réorientation des terminaux de communication sur papier en tant qu'interfaces utilisateurs informatiques. Dans l'ère Unix et C initiale qui était le télétype ASR-33.

Cet appareil était lent (10 cps) et bruyant et laid et sa vue du jeu de caractères ASCII se terminait à 0x5f, donc il n'avait (regardez attentivement la photo) aucune des clés:

{ | } ~ 

Les trigraphes ont été définis pour résoudre un problème spécifique. L'idée était que les programmes C pouvaient utiliser le sous-ensemble ASCII trouvé sur l'ASR-33 et dans d'autres environnements sans les valeurs ASCII élevées.

Votre exemple est en fait deux ??!, chaque signification |, donc le résultat est ||.

Cependant, les gens qui écrivent le code C presque par définition avaient un équipement moderne, 1 donc je suppose: quelqu'un montrer amusant themself ou, en laissant une sorte d'oeuf de Pâques dans le code pour vous de trouver.

Cela a bien sûr fonctionné, cela a conduit à une question SO très populaire.

Télétype ASR-33

                                            Télétype ASR-33


1. D'ailleurs, les trigraphes ont été inventés par le comité ANSI, qui s'est réuni pour la première fois après que C est devenu un succès fulgurant, donc aucun des codes ou codeurs C d'origine ne les aurait utilisés.

DigitalRoss
la source
18
Ce n'est pas le seul cas de caractères manquants, au clavier et au jeu de caractères. Le Commodore 64 est susceptible d'être plus familier à beaucoup de gens dans la fin de la trentaine et vers le haut - les jeux de caractères affichés manquaient tous les deux d'accolades (et probablement aussi la barre et le tilde) - dans ce cas parce que le "ASCII" n'était pas ASCII . Dans ECMA-6 (presque toujours appelé ASCII, mais pas US-ASCII), il y avait 18 codes spécifiques à une région, mais je ne sais pas de quels codes il s'agissait. La seule chose que je peux dire avec certitude - dans le "ASCII" britannique, a #été remplacé par £. Dans d'autres régions, peut-être que "ASCII" n'avait pas d'appareil
orthopédique,
7
Le jeu de caractères ATASCII similaire pour les ordinateurs Atari 8 bits manquait également {} ainsi que ~ et `.
dan04
42
Voir ces deux articles Wikipedia. Je suis à peu près assez vieux pour me souvenir encore de l'ère des jeux de caractères nationaux 7 bits (bien que je sois sûr qu'ils persistent encore dans certains coins sombres non balayés), et le livre dont j'ai appris le C pour la première fois a jugé nécessaire d'avertir de la possibilité de if (x || y) { a[i] = '\0'; }ressembler if (x öö y) ä aÄiÅ = 'Ö0'; åau mauvais jeu de caractères.
Ilmari Karonen
9
Une autre note historique intéressante est qu'Unix (qui était la grande plateforme sur laquelle C est monté) a peut-être été le premier système d'importance (et peut-être le premier dans l'ensemble) pour les valeurs alphabétiques par défaut en minuscules plutôt qu'en majuscules. Bien que je n'aie pas vu de mes propres yeux de nombreux systèmes contemporains, je pense que c'était un vrai signe de sophistication. En plus d'être vraiment le seul système d'exploitation décent, Unix a également converti votre majuscule en minuscule, plutôt que l'inverse. Ces gars étaient vraiment cool.
DigitalRoss
16
Drôle d'histoire, je dois vous dire ... le compilateur XL Fortran de la station de travail IBM RS / 6000 a été développé à partir du compilateur XL C. Dans les premières versions, ils sont accidentellement partis dans le traitement du trigraphe, il y avait donc des séquences de caractères Fortran légitimes (dans une chaîne littérale, IIRC) qui ont été mal interprétées comme des trigraphes C, conduisant à des bugs intéressants!
Phil Perry
166

Il est un C trigraphes . ??!est |, est donc ??!??!l'opérateur||

Joel Falcou
la source
5
le trigraphe vient d'une période où certains claviers n'avaient pas toutes les touches qu'ils ont maintenant. Cela se passe également quand un éditeur de texte a réservé des caractères spéciaux pour des choses spéciales. C'est surtout une relique du passé et un facilitateur de quizz;)
Joel Falcou
5
Parce que certains claviers n'ont apparemment pas de "|" certaines personnes n'ont donc pas d'autre choix que de couper le clavier à plusieurs reprises jusqu'à ce qu'un trigraphe se produise qui leur donne les symboles dont elles ont besoin.
Chouette
Et puis il y a le <iso646.h>fichier d'en-tête.
David R Tribble
149

Comme déjà indiqué, il ??!??!y a essentiellement deux trigraphes ( ??!et ??!encore une fois) assemblés qui sont remplacés-traduits en ||, c'est -à -dire le OU logique , par le préprocesseur.

Le tableau suivant contenant chaque trigraphe devrait aider à lever l'ambiguïté des combinaisons de trigraphes alternatives:

Trigraph   Replaces

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

Source: C: A Reference Manual 5th Edition

Donc, un trigraphe qui ressemble ??(??)finira par correspondre [], ??(??)??(??)sera remplacé par [][]et ainsi de suite, vous avez l'idée.

Étant donné que les trigraphes sont substitués pendant le prétraitement, vous pouvez utiliser cpppour obtenir vous-même une vue de la sortie, à l'aide d'un trigr.cprogramme stupide :

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

et le traiter avec:

cpp -trigraphs trigr.c 

Vous obtiendrez une sortie console de

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

Comme vous pouvez le constater, l'option -trigraphsdoit être spécifiée, sinon cppun avertissement sera émis; cela indique comment les trigraphes sont une chose du passé et sans valeur moderne autre que de confondre les gens qui pourraient les croiser .


Quant à la justification de l'introduction des trigraphes, elle est mieux comprise lorsque l'on examine la section historique de l'ISO / CEI 646 :

L'ISO / CEI 646 et son prédécesseur ASCII (ANSI X3.4) ont largement approuvé les pratiques existantes concernant les codages de caractères dans l'industrie des télécommunications.

Étant donné que l'ASCII ne fournissait pas un certain nombre de caractères nécessaires pour les langues autres que l'anglais, un certain nombre de variantes nationales ont été créées pour remplacer certains caractères moins utilisés par ceux nécessaires .

(c'est moi qui souligne)

Ainsi, en substance, certains caractères nécessaires (ceux pour lesquels il existe un trigraphe) ont été remplacés dans certaines variantes nationales. Cela conduit à la représentation alternative utilisant des trigraphes composés de caractères que d'autres variantes avaient encore autour.

Dimitris Fasarakis Hilliard
la source