significa que prioridad precedencia operadores operador logicos jerarquia condicional asignacion c obfuscation literals bit-shift digraphs

que - precedencia de operadores en c



¿Qué es el operador>>>=en C? (3)

Dado por un colega como un rompecabezas, no puedo entender cómo este programa C realmente compila y ejecuta. ¿Qué es este >>>= operador y el extraño literal 1P1 ? He probado en Clang y GCC. No hay advertencias y la salida es "???"

#include <stdio.h> int main() { int a[2]={ 10, 1 }; while( a[ 0xFULL?''/0'':-1:>>>=a<:!!0X.1P1 ] ) printf("?"); return 0; }


Es un código bastante oscuro que involucra digraphs , a saber, <: y :> que son tokens alternativos para [ y ] respectivamente. También hay algún uso del operador condicional . También hay un operador de cambio de bits , la asignación de desplazamiento a la derecha >>= .

Esta es una versión más legible:

while( a[ 0xFULL ? ''/0'' : -1 ] >>= a[ !!0X.1P1 ] )

y una versión aún más legible, que reemplaza las expresiones en [] para los valores que resuelven:

while( a[0] >>= a[1] )

Reemplazar a[0] y a[1] para sus valores debería facilitar la tarea de averiguar qué está haciendo el bucle, es decir, el equivalente de:

int i = 10; while( i >>= 1)

que simplemente realiza la división (entero) por 2 en cada iteración, produciendo la secuencia 5, 2, 1 .


La línea:

while( a[ 0xFULL?''/0'':-1:>>>=a<:!!0X.1P1 ] )

contiene los digraphs :> y <: que se traducen a ] y [ respectivamente, por lo que es equivalente a:

while( a[ 0xFULL?''/0'':-1 ] >>= a[ !!0X.1P1 ] )

El literal 0xFULL es el mismo que 0xF (que es hexadecimal para 15 ); el ULL solo especifica que es un literal unsigned long long . En cualquier caso, como booleano es cierto, entonces 0xFULL ? ''/0'' : -1 0xFULL ? ''/0'' : -1 evalúa como ''/0'' , que es un literal de carácter cuyo valor numérico es simplemente 0 .

Mientras tanto, 0X.1P1 es un literal de coma flotante hexadecimal igual a 2/16 = 0.125. En cualquier caso, al no ser cero, también es cierto como booleano, ¡por lo que se anula dos veces con !! de nuevo produce 1 . Por lo tanto, todo se simplifica a:

while( a[0] >>= a[1] )

El operador >>= es una asignación compuesta que bit-desplaza su operando izquierdo a la derecha por el número de bits dado por el operando derecho, y devuelve el resultado. En este caso, el operando derecho a[1] siempre tiene el valor 1 , por lo que es equivalente a:

while( a[0] >>= 1 )

o equivalente:

while( a[0] /= 2 )

El valor inicial de a[0] es 10. Después de cambiar a la derecha una vez, se convierte en 5, luego (redondeando hacia abajo) 2, luego 1 y finalmente 0, en cuyo punto finaliza el bucle. Por lo tanto, el cuerpo del bucle se ejecuta tres veces.


Vayamos a través de la expresión de izquierda a derecha:

a[ 0xFULL?''/0'':-1:>>>=a<:!!0X.1P1 ]

Lo primero que noté es que estamos usando el operador ternario del uso de ? . Así que la subexpresión:

0xFULL ? ''/0'' : -1

está diciendo "si 0xFULL es distinto de cero, devuelva ''/0'' , de lo contrario -1 . 0xFULL es un literal hexadecimal con el sufijo long-long sin signo , lo que significa que es un literal hexadecimal de tipo unsigned long long . Eso realmente no importa sin embargo, porque 0xF puede caber dentro de un entero normal.

Además, el operador ternario convierte los tipos de los términos segundo y tercero a su tipo común. ''/0'' se convierte a int , que es solo 0 .

El valor de 0xF es mucho más grande que cero, por lo que pasa. La expresión ahora se convierte en:

a[ 0 :>>>=a<:!!0X.1P1 ]

A continuación, :> es un digraph . Es una construcción que se expande a ] :

a[0 ]>>=a<:!!0X.1P1 ]

>>= es el operador de cambio a la derecha firmado, podemos separarlo de un espacio para hacerlo más claro.

Además, <: es un dígrafo que se expande a [ :

a[0] >>= a[!!0X.1P1 ]

0X.1P1 es un literal hexadecimal con un exponente. Pero no importa el valor, el !! De cualquier cosa que no sea cero es verdad. 0X.1P1 es 0.125 que es distinto de cero, por lo que se convierte en:

a[0] >>= a[true] -> a[0] >>= a[1]

El >>= es el operador de cambio a la derecha firmado. Cambia el valor de su operando izquierdo desplazando sus bits hacia adelante por el valor en el lado derecho del operador. 10 en binario es 1010 . Así que aquí están los pasos:

01010 >> 1 == 00101 00101 >> 1 == 00010 00010 >> 1 == 00001 00001 >> 1 == 00000

>>= devuelve el resultado de su operación, por lo tanto, mientras el desplazamiento de a[0] no sea cero por cada vez que sus bits se desplacen uno por uno, el bucle continuará. El cuarto intento es cuando a[0] convierte en 0 , por lo que el bucle nunca se ingresa.

Como resultado ? Se imprime tres veces.