¿Qué es el operador>= en C?


Dado por un colega como un rompecabezas, no puedo averiguar cómo este programa C realmente compila y se ejecuta. ¿Qué es este >>>= operador y el extraño 1P1 literal? 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;
}
Author: TLama, 2014-08-26

3 answers

La línea:

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

Contiene los dígrafos :> 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 lo mismo que 0xF (que es hexadecimal para 15); el ULL solo especifica que es un unsigned long long literal. En cualquier caso, como un booleano es verdadero, por lo que 0xFULL ? '\0' : -1 evalúa a '\0', que es un carácter literal cuyo valor numérico es simplemente 0.

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

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

El operador >>= es una asignación compuesta que 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, equivalentemente:

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 termina el bucle. Por lo tanto, el cuerpo del bucle se ejecuta tres veces.

 461
Author: Ilmari Karonen,
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-05-23 11:54:34

Es un código bastante oscuro que involucra dígrafos, es decir, <: y :> que son tokens alternativos para [ y ] respectivamente. También hay un cierto uso de la operador condicional. También hay un operador de desplazamiento 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, reemplazando las expresiones en el [] para los valores ellos deciden:

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

La sustitución de a[0] y a[1] para sus valores debe hacer que sea fácil averiguar lo que el bucle está haciendo, es decir, el equivalente de:

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

Que simplemente realiza una división (entera) por 2 en cada iteración, produciendo la secuencia 5, 2, 1.

 67
Author: juanchopanza,
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
2014-08-26 18:56:31

Repasemos la expresión de izquierda a derecha:

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

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

0xFULL ? '\0' : -1

Está diciendo "si 0xFULL es distinto de cero, devuelve '\0', de lo contrario -1. 0xFULLes un literal hexadecimal con el sufijo largo-largo sin signo - lo que significa que es un literal hexadecimal de tipo unsigned long long. Sin embargo, eso no importa realmente, porque 0xF puede caber dentro de un entero.

Además, el operador ternario convierte los tipos del segundo y tercer término a su tipo común. '\0' se convierte entonces a int, que es solo 0.

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

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

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

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

>>= es el operador de desplazamiento derecho firmado, podemos espaciarlo desde a 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 verdadero. 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 desplazamiento derecho con signo. 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, así que mientras shifting a[0] permanezca distinto de cero por cada vez que sus bits se desplacen a la derecha por uno, el bucle continuará. El cuarto intento es donde a[0] se convierte en 0, por lo que el bucle nunca se ingresa.

Como resultado, ? se imprime tres veces.

 41
Author: 0x499602D2,
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
2014-10-30 02:41:41