¿Qué es el operador >>>= en C?

7 minutos de lectura

avatar de usuario
CustomCalc

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

#include <stdio.h>

int main()
{
    int a[2]={ 10, 1 };

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

    return 0;
}

  • Algunos de esos son dígrafos.

    – juanchopanza

    25 de agosto de 2014 a las 22:04

  • @Kay, no en este caso: :> = ]entonces un[…] >> = un[…]

    – Adriano Repetti

    25 de agosto de 2014 a las 22:08


  • @Marc No creo que pueda ser “>> >=” porque eso no se compilaría, sin embargo, el código anterior realmente se compila.

    – CustomCalc

    25 de agosto de 2014 a las 22:08

  • los 0x.1P1 es un literal hexadecimal con un exponente. los 0x.1 es la parte numérica, o 1/16 aquí. El número después de la ‘P’ es la potencia de dos por la que se multiplica el número. Asi que 0x.1p1 es realmente 1/16 * 2, o 1/8. Y si te estabas preguntando sobre 0xFULL eso es solo 0xFy ULL es el sufijo de un unsigned long long

    – brazos de gato

    25 de agosto de 2014 a las 22:10


  • Sintaxis C: material interminable para expertos y amantes de las trivialidades, pero en última instancia, no es tan importante.

    – KerrekSB

    25 de agosto de 2014 a las 22:41

avatar de usuario
ilmari karonen

La línea:

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

contiene la dígrafos :> y <:que se traducen en ] y [ respectively, so it’s equivalent to:

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

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

Mientras tanto, 0X.1P1 es un literal de punto 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 !! vuelve a producir 1. Por lo tanto, todo se simplifica a:

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

El operador >>= es un asignación compuesta que desplaza los bits de su operando izquierdo a la derecha en la cantidad de bits dados por el operando derecho y devuelve el resultado. En este caso, el operando derecho a[1] siempre tiene el valor 1por 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 desplazarse 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.

  • ¿Podría por favor dar más detalles sobre el P en 0X.1P1.

    – está bien

    25 de agosto de 2014 a las 22:20

  • @Kay: Es lo mismo que e en 10e5excepto que tienes que usar p para literales hexadecimales porque e es un dígito hexadecimal.

    -Dietrich Epp

    25 de agosto de 2014 a las 22:21

  • @Kay: Los literales flotantes hexadecimales son parte de C99, pero GCC también los acepta en código C++. Como señala Dietrich, la p separa la mantisa y el exponente, al igual que el e en notación flotante científica normal; una diferencia es que, con los flotantes hexadecimales, la base de la parte exponencial es 2 en lugar de 10, por lo que 0x0.1p1 es igual a 0x0.1 = 1/16 veces 2¹ = 2. (En cualquier caso, nada de eso importa aquí; cualquier valor distinto de cero funcionaría igual de bien allí).

    – Ilmari Karonen

    25 de agosto de 2014 a las 22:27


  • Menor: Cierto '\0' es un int literal. Probar printf("%zu %zu\n", sizeof ('\0'), sizeof (char));

    – chux – Reincorporar a Monica

    25 de agosto de 2014 a las 22:46


  • @chux: Aparentemente, eso depende de si el código se compila como C o (como se etiquetó originalmente) C++. Pero arreglé el texto para decir “caracter literal” en lugar de “char literal”, y agregó un enlace de Wikipedia. ¡Gracias!

    – Ilmari Karonen

    25 de agosto de 2014 a las 23:24


avatar de usuario
juanchopanza

Es un código bastante oscuro que involucra dígrafosa saber <: y :> que son tokens alternativos para [ and ] respectivamente. También hay algún uso de la operador condicional. También hay una operador de cambio de bitsla asignación de turno 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 [] por los valores que resuelven:

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

reemplazando a[0] y a[1] ya que sus valores deberían hacer que sea fácil averiguar qué está haciendo el bucle, es decir, el equivalente a:

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.

  • No lo ejecuté, ¿esto no produciría ????aunque, en lugar de ??? como obtuvo el OP? (Eh.) codepad.org/nDkxGUNi lo hace producir ???.

    – Jongware

    25 de agosto de 2014 a las 22:34


  • @Jongware, los 10 se dividieron en la primera iteración. Entonces, los valores que evalúa el ciclo son 5, 2, 1 y 0. Por lo tanto, solo se imprime 3 veces.

    – MysticXG

    25 de agosto de 2014 a las 22:49

avatar de usuario
david g

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 del uso de ?. Entonces la subexpresión:

0xFULL ? '\0' : -1

está diciendo “si 0xFULL es distinto de cero, regresa '\0'de lo contrario -1. 0xFULL es un literal hexadecimal con el sufijo largo-largo sin firmar – lo que significa que es un literal hexadecimal de tipo unsigned long long. Sin embargo, eso realmente no importa, porque 0xF puede caber dentro de un entero regular.

Además, el operador ternario convierte los tipos del segundo y tercer término a su tipo común. '\0' luego se convierte en intque 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 ]

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

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

>>= es el operador de desplazamiento a la derecha con signo, podemos espaciarlo desde a para que quede mas claro

Es má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]

los >>= es el operador de desplazamiento a la derecha 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, por lo que siempre que se cambie a[0] permanece distinto de cero porque cada vez que sus bits se desplazan uno a la derecha, el ciclo continuará. El cuarto intento es donde a[0] se convierte 0por lo que nunca se entra en el bucle.

Como resultado, ? se imprime tres veces.

  • :> es un dígrafo, no un trigrafo. No es manejado por el preprocesador, simplemente es reconocido como un token equivalente a ].

    –Keith Thompson

    25 de agosto de 2014 a las 22:33

  • @KeithThompson Gracias

    – David G.

    25 de agosto de 2014 a las 22:36

  • El operador ternario (?:) tiene un tipo que es el tipo común del segundo y tercer término. El primer término es siempre un condicional y tiene un tipo bool. Dado que tanto el segundo como el tercer término tienen tipo int el resultado de la operación ternaria será intno unsigned long long.

    – Corey

    26 de agosto de 2014 a las 6:25

  • @KeithThompson podría ser manejado por el preprocesador. El preprocesador tiene que saber acerca de los dígrafos porque # y ## tener formas de dígrafo; no hay nada que impida que una implementación traduzca dígrafos a no dígrafos durante las primeras fases de traducción

    –MM

    26 de agosto de 2014 a las 13:15

  • @MattMcNabb Ha pasado mucho tiempo desde que tuve que saber esto, pero IIRC, como consecuencia de otros requisitos, los dígrafos deben permanecer en su forma de dígrafo hasta el punto en que los tokens pp se conviertan en tokens (justo al comienzo de la fase de traducción 7).

    – zwol

    26 de agosto de 2014 a las 13:43

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad