¿Qué hace el C ??! ??! operador hacer?

Vi una línea de C que se veía así:

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

Se compiló correctamente y parece funcionar bien. Parece que está comprobando si se ha producido un error, y si lo tiene, lo maneja. Pero no estoy muy seguro de lo que realmente está haciendo o cómo lo está haciendo. Parece que el progtwigdor intenta express sus sentimientos sobre los errores.

Nunca he visto el ??!??! antes en cualquier lenguaje de progtwigción, y no puedo encontrar documentación para él en ninguna parte. (Google no ayuda con términos de búsqueda como ??!??! ). ¿Qué hace y cómo funciona la muestra del código?

??! es un trigraph que se traduce en | . Entonces dice:

 !ErrorHasOccured() || HandleError(); 

que, debido a un cortocircuito, es equivalente a:

 if (ErrorHasOccured()) HandleError(); 

Gurú de la semana (se trata de C ++ pero relevante aquí), donde recogí esto.

El origen posible de los trigrafos o como @DwB señala en los comentarios es más probable debido a que EBCDIC es difícil (nuevamente). Esta discusión sobre la placa de IBM developerworks parece apoyar esa teoría.

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

Las secuencias de trigrafos permiten la entrada de caracteres que no están definidos en el Conjunto de códigos invariantes, como se describe en ISO / IEC 646, que es un subconjunto del conjunto de códigos ASCII de EE. UU. De siete bits.

Bueno, por qué esto existe en general es probablemente diferente de por qué existe en tu ejemplo.

Todo comenzó hace medio siglo con la reutilización de terminales de comunicación en papel como interfaces de usuario de computadora. En la era inicial de Unix y C, ese era el teletipo ASR-33.

Este dispositivo era lento (10 cps) y ruidoso y feo y su vista del conjunto de caracteres ASCII terminaba en 0x5f, por lo que tenía (mira de cerca la foto) ninguna de las teclas:

 { | } ~ 

Los trigrafos se definieron para solucionar un problema específico. La idea era que los progtwigs C podrían usar el subconjunto ASCII que se encuentra en el ASR-33 y en otros entornos que carecen de los altos valores ASCII.

Tu ejemplo es en realidad dos ??! , cada significado | , entonces el resultado es || .

Sin embargo, las personas que escriben el código C casi por definición tenían equipo moderno, 1 así que mi suposición es: alguien presumiendo o divirtiéndose, dejando una especie de huevo de Pascua en el código para que usted pueda encontrar.

Sin duda funcionó, condujo a una pregunta SO muy popular.

Teletipo ASR-33

Teletipo ASR-33


1. Para el caso, los trigrafos fueron inventados por el comité ANSI, que se reunió por primera vez después de que C se convirtió en un éxito desenfrenado, por lo que ninguno de los codificadores C originales o codificadores los habrían usado.

Es un trigrafo C. ??! es | , entonces ??!??! es el operador ||

Como ya se dijo ??!??! es esencialmente dos trigrafos ( ??! y ??! otra vez) mushed juntos que se reemplazan -traducidos a || , es decir, el OR lógico , por el preprocesador.

La siguiente imagen que contiene todos los trigrafos debería ayudar a eliminar la ambigüedad de las combinaciones de trigrafos alternativos:

enter image description here (Imagen tomada de C: Un Manual de Referencia 5ta Edición )

Entonces, un trigrafo que se ve como ??(??) eventualmente mapeará a [] , ??(??)??(??) se reemplazará por [][] y así sucesivamente, se entiende la idea.

Como los trigrafos se sustituyen durante el preprocesamiento, puede usar cpp para obtener una vista de la salida usted mismo, utilizando un progtwig tonto trigr.c :

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

y procesarlo con:

 cpp -trigraphs trigr.c 

Obtendrás una salida de consola de

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

Como puede observar, se debe especificar la opción -trigraphs o, de lo contrario, cpp emitirá una advertencia; esto indica cómo los trigrafos son una cosa del pasado y de ningún valor moderno que no sea confundir a las personas que puedan toparse con ellos .


En cuanto a la razón de ser de la introducción de los trigrafos, se comprende mejor al mirar la sección de Historia de ISO/IEC 646 :

ISO / IEC 646 y su predecesor ASCII (ANSI X3.4) respaldaron ampliamente la práctica existente con respecto a las codificaciones de caracteres en la industria de las telecomunicaciones.

Como ASCII no proporcionó una cantidad de caracteres necesarios para idiomas distintos del inglés, se hicieron varias variantes nacionales que sustituyeron algunos caracteres menos utilizados con los necesarios .

(énfasis mío)

Entonces, en esencia, algunos caracteres necesarios (aquellos para los cuales existe un trigrafo) fueron reemplazados en ciertas variantes nacionales. Esto condujo a la representación alternativa usando trigrafos compuestos de caracteres que otras variantes todavía tenían alrededor.