¿Qué hace la C! ¿operadora?


Vi una línea de Do que se parecía a esto:

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

Se compiló correctamente y parece funcionar bien. Parece que está comprobando si se ha producido un error, y si lo ha hecho, lo maneja. Pero no estoy muy seguro de lo que está haciendo realmente o cómo lo está haciendo. Parece que el programador está tratando de expresar sus sentimientos acerca de los errores.

Nunca he visto el ??!??! antes en ningún lenguaje de programación, y no puedo encontrar documentación para él en ningún lugar. (Google no ayuda con términos de búsqueda como ??!??!). ¿Qué hace y cómo funciona el ejemplo de código?

Author: Nathaniel Ford, 2011-10-19

4 answers

??! es un trigraph que se traduce como |. Así que dice:

!ErrorHasOccured() || HandleError();

Que, debido al cortocircuito, es equivalente a:

if (ErrorHasOccured())
    HandleError();

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

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

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

Las secuencias de trigraph permiten la entrada de caracteres que no están definidos en el Código Invariante Establecido como descrito en ISO / IEC 646, que es un subconjunto del conjunto de código ASCII de siete bits de EE.

 1341
Author: user786653,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-21 08:52:52

Bueno, por qué esto existe en general es probablemente diferente de por qué 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 que era el teletipo ASR-33.

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

{ | } ~ 

Los trigraphs se definieron para solucionar un problema específico. La idea era que los programas C pudieran usar el subconjunto ASCII encontrado en el ASR-33 y en otros entornos que no tuvieran los valores ASCII altos.

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

Sin embargo, las personas que escriben código C casi por definición tenían equipos modernos,1 así que mi conjetura es: alguien presumiendo o divirtiéndose, dejando una especie de huevo de Pascua en el código para que lo encuentres.

Seguro que funcionó, llevó a una pregunta muy popular.

Teletipo ASR-33

Teletipo ASR-33


1. Para el caso, los trigraphs fueron inventados por el comité ANSI, que se reunió por primera vez después de C convertirse en un éxito fuera de control, por lo que ninguno de los códigos originales de C o codificadores los habría utilizado.
 362
Author: DigitalRoss,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-15 18:02:28

Es un C trigraph. ??! es |, así que ??!??! es el operador ||

 143
Author: Joel Falcou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-08 22:15:38

Como ya se ha dicho ??!??! es esencialmente dos trigraphs (??! y ??! otra vez) mushed juntos que son reemplazados-traducido a ||, es decir, el Lógica O, por el preprocesador.

La siguiente imagen que contiene todos los trigraphs debería ayudar a desambiguar combinaciones alternativas de trigraph:

introduzca la descripción de la imagen aquí (Imagen tomada de C: Un Manual de Referencia 5a Edición)

Así que un trigraph eso parece que ??(??) eventualmente se mapeará a [], ??(??)??(??) será reemplazado por [][] y así sucesivamente, usted consigue la idea.

Dado que los trigraphs se sustituyen durante el preprocesamiento, podría usar cpp para obtener una vista de la salida usted mismo, usando un tonto trigr.c programa:

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 observar, la opción -trigraphs debe especificarse o de lo contrario cpp emitirá una advertencia; esto indica cómo los trigraphs son una cosa del pasado y de ningún valor moderno más que confundir a la gente que podría toparse con ellos.


Como el razonamiento detrás de la introducción de trigraphs, se entiende mejor cuando se mira en el Historia sección 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 un número de caracteres necesarios para idiomas distintos del inglés, se hicieron varias variantes nacionales que sustituyeron algunos caracteres menos utilizados por los necesarios.

(énfasis mío)

Así que, en esencia, algunos caracteres necesarios (aquellos para los que existe un trigraph) fueron reemplazados en ciertas variantes nacionales. Esto conduce a la representación alternativa utilizando trigramas compuestos de caracteres que otros las variantes todavía tenían alrededor.

 88
Author: Jim Fasarakis Hilliard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-18 23:49:19