operators operadores - ¿Qué hace el C ??! ??! operador hacer?




en logicos (5)

Es un trigraph C ??! es | , entonces ??!??! es el operador ||

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

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

Se compiló correctamente y parece funcionar bien. Parece que está verificando si ha ocurrido un error, y si lo ha hecho, lo maneja. Pero no estoy realmente seguro de lo que realmente está haciendo 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 cualquier lenguaje de programación, y no puedo encontrar documentación para ello en ningún lugar. (Google no ayuda con los términos de búsqueda como ??!??! ). ¿Qué hace y cómo funciona el ejemplo de código?


Como ya se ha dicho ??!??! es esencialmente dos trigraphs ( ??! y ??! otra vez) agrupados que se reemplazan-traducen a || , es decir, el OR lógico , por el preprocesador.

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

(Imagen tomada de C: A Manual de referencia 5ª edición )

Así que un trigraph que se parece a ??(??) eventualmente se asignará a [] , ??(??)??(??) se reemplazará por [][] y así sucesivamente, se obtiene la idea.

Como los trigraphs se sustituyen durante el preproceso, puede usar cpp para obtener una vista de la salida, utilizando un programa trigr.c tonto:

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

y procesándolo con:

cpp -trigraphs trigr.c 

Obtendrás una salida de consola de

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

Como puede observar, la opción -trigraphs debe estar especificada o de lo contrario, cpp emitirá una advertencia; esto indica que los trigraphs son cosa del pasado y que no tienen valor moderno, aparte de confundir a las personas que podrían toparse con ellos .

En cuanto a la razón detrás de la introducción de trigraphs, se entiende mejor al mirar la sección de Historia de ISO/IEC 646 :

ISO / IEC 646 y su antecesor 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 otros idiomas además del inglés, se hicieron varias variantes nacionales que sustituyeron a algunos caracteres menos usados ​​con los necesarios .

(énfasis mío)

Entonces, en esencia, algunos caracteres necesarios (aquellos para los cuales existe un trigraph) fueron reemplazados en ciertas variantes nacionales. Esto condujo a la representación alternativa utilizando trigráficos compuestos por caracteres que aún tenían otras variantes.


??! es un trigraph que se traduce a | . Eso dice:

!ErrorHasOccured() || HandleError();

el cual, por cortocircuito, es equivalente a:

if (ErrorHasOccured())
    HandleError();

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

Posible origen de trigraphs o como @DwB señala en los comentarios, es más probable que EBCDIC sea difícil (otra vez). This discusión en el tablero de IBM developerworks parece apoyar esa teoría.

De ISO / CEI 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 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 a por qué existe en su ejemplo.

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

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

{ | } ~ 

Los trigraphs fueron definidos para solucionar un problema específico. La idea era que los programas en C podrían usar el subconjunto ASCII que se encuentra en el ASR-33 y en otros entornos que no tienen los valores altos de ASCII.

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

Sin embargo, las personas que escribían el código C casi por definición tenían equipo moderno, 1 así que supongo: alguien que se muestra o se divierte, dejando una especie de huevo de Pascua en el código para que lo encuentres.

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

Teletipo ASR-33

1. En ese caso, los trigraphs fueron inventados por el comité ANSI, que se reunió por primera vez después de que C se convirtió en un gran éxito, por lo que ninguno de los codificadores o el código C original los habría utilizado.


Las tres reglas básicas de sobrecarga de operadores en C ++

Cuando se trata de la sobrecarga de operadores en C ++, hay tres reglas básicas que debe seguir . Al igual que con todas estas reglas, de hecho hay excepciones. Algunas veces las personas se han desviado de ellos y el resultado no fue un código malo, pero tales desviaciones positivas son pocas y distantes entre sí. Por lo menos, 99 de cada 100 desviaciones que he visto estaban injustificadas. Sin embargo, podría haber sido 999 de 1000. Así que es mejor que te limites a las siguientes reglas.

  1. Siempre que el significado de un operador no sea claro e indiscutible, no debe sobrecargarse. En su lugar, proporcionar una función con un nombre bien elegido.
    Básicamente, la primera y más importante regla para sobrecargar a los operadores, en su corazón, dice: No lo hagas . Esto puede parecer extraño, porque hay mucho que saber sobre la sobrecarga de operadores y, por lo tanto, muchos artículos, capítulos de libros y otros textos tratan todo esto. Pero a pesar de esta evidencia aparentemente obvia, hay solo unos pocos casos sorprendentemente apropiados donde la sobrecarga de operadores es apropiada . La razón es que en realidad es difícil entender la semántica detrás de la aplicación de un operador, a menos que el uso del operador en el dominio de la aplicación sea bien conocido e indiscutible. Contrariamente a la creencia popular, este casi nunca es el caso.

  2. Siempre apegarse a la semántica conocida del operador.
    C ++ no plantea limitaciones en la semántica de los operadores sobrecargados. Su compilador aceptará felizmente el código que implementa el operador binario + para restar de su operando derecho. Sin embargo, los usuarios de tal operador nunca sospecharían que la expresión a + b restara a de b . Por supuesto, esto supone que la semántica del operador en el dominio de la aplicación es indiscutible.

  3. Siempre proporcionar todo de un conjunto de operaciones relacionadas.
    Los operadores están relacionados entre sí y con otras operaciones. Si su tipo admite a + b , los usuarios también podrán llamar a += b . Si admite el incremento de prefijo ++a , esperarán que a++ funcione también. Si pueden verificar si a < b , seguramente esperarán también poder verificar si a > b . Si pueden copiar y construir su tipo, esperan que la asignación también funcione.

Continuar con la decisión entre el miembro y el no miembro .







c operators trigraphs