Lo que hace el ??!??! operador hace en C?

Resuelto Peter Olson asked hace 12 años • 4 respuestas

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 es así, lo gestiona. Pero no estoy realmente seguro de qué está haciendo realmente ni cómo lo está haciendo. Parece que el programador está intentando expresar sus sentimientos sobre los errores.

Nunca había visto esto ??!??!antes en ningún lenguaje de programación y no puedo encontrar documentación sobre él en ninguna parte. (Google no ayuda con términos de búsqueda como ??!??!). ¿Qué hace y cómo funciona el código de muestra?

Peter Olson avatar Oct 19 '11 23:10 Peter Olson
Aceptado

??!es un trígrafo que se traduce como |. Entonces dice:

!ErrorHasOccured() || HandleError();

que por cortocircuito equivale a:

if (ErrorHasOccured())
    HandleError();

Gurú de la semana (trata con C++ pero es relevante aquí), donde aprendí esto.

Posible origen de los trígrafos o, como señala @DwB en los comentarios, es más probable que se deba a que EBCDIC sea difícil (nuevamente). Esta discusión en la junta de desarrolladores de IBM parece respaldar esa teoría.

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

Las secuencias de trígrafo 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 estadounidense de siete bits.

user786653 avatar Oct 19 '2011 16:10 user786653

Bueno, el motivo por el que esto existe en general probablemente sea diferente al motivo por el que existe en su ejemplo.

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

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

{ | } ~ 

Los trígrafos se definieron para solucionar un problema específico. La idea era que los programas en C pudieran usar el subconjunto ASCII que se encuentra en el ASR-33 y en otros entornos que carecían de valores ASCII altos.

Su ejemplo es en realidad dos de ??!, cada significado |, por lo que el resultado es ||.

Sin embargo, las personas que escribían código C casi por definición tenían equipos modernos, 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 lo encuentre.

Seguro que funcionó, condujo a una pregunta SO muy popular.

Teletipo ASR-33

                                            Teletipo ASR-33


1. De hecho, los trígrafos fueron inventados por el comité ANSI, que se reunió por primera vez después de que C se convirtiera en un gran éxito, por lo que ninguno de los códigos o codificadores originales de C los habría usado.

DigitalRoss avatar Oct 19 '2011 21:10 DigitalRoss

Es un trígrafo C. ??!es |, también lo ??!??!es el operador||

Joel Falcou avatar Oct 19 '2011 16:10 Joel Falcou

Como ya se indicó, ??!??!son esencialmente dos trígrafos ( ??!y nuevamente) mezclados que el preprocesador ??!reemplaza y traduce a ||, es decir, el OR lógico .

La siguiente tabla que contiene cada trígrafo debería ayudar a eliminar la ambigüedad de las combinaciones de trígrafos alternativos:

Trigraph   Replaces

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

Fuente: C: Manual de referencia, quinta edición

Entonces, un trígrafo que parece ??(??)eventualmente se asignará a [], ??(??)??(??)será reemplazado por [][]y así sucesivamente, ya entiendes la idea.

Dado que los trígrafos se sustituyen durante el preprocesamiento, podrías usarlos cpppara obtener una vista del resultado tú mismo, usando un trigr.cprograma tonto:

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

y procesándolo con:

cpp -trigraphs trigr.c 

Obtendrá una salida de consola de

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

Como puede notar, -trigraphsse debe especificar la opción o de lo contrario cppemitirá una advertencia; esto indica que los trígrafos son cosa del pasado y no tienen ningún valor moderno más que confundir a las personas que podrían toparse con ellos .


En cuanto a la razón fundamental detrás de la introducción de trígrafos, se comprende mejor si se mira la sección de historia de ISO/IEC 646 :

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

Como ASCII no proporcionaba una cantidad de caracteres necesarios para idiomas distintos del inglés, se crearon una serie de variantes nacionales que sustituyeron algunos caracteres menos utilizados por otros necesarios .

(el énfasis es mío)

Entonces, en esencia, algunos caracteres necesarios (aquellos para los cuales existe un trígrafo) fueron reemplazados en ciertas variantes nacionales. Esto lleva a la representación alternativa utilizando trígrafos compuestos por caracteres que otras variantes todavía tenían.