Dado por un colega como un rompecabezas, no puedo entender 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;
}
0x.1P1
es un literal hexadecimal con un exponente. El0x.1
es la parte del número, o 1/16 aquí. El número después de la 'P' es la potencia de dos, el número se multiplica por. Entonces0x.1p1
es realmente 1/16 * 2, o 1/8. Y si te estabas preguntando acerca de0xFULL
eso es justo0xF
, yULL
es el sufijo para ununsigned long long
Respuestas:
La línea:
contiene los dígrafos
:>
y<:
, que se traducen a]
y[
respectivamente, por lo que es equivalente a:El literal
0xFULL
es el mismo que0xF
(que es hexadecimal15
); elULL
solo especifica que es ununsigned long long
literal . En cualquier caso, como booleano es cierto, así se0xFULL ? '\0' : -1
evalúa como'\0'
, que es un carácter literal cuyo valor numérico es simplemente0
.Mientras tanto,
0X.1P1
es un literal de coma flotante hexadecimal igual a 2/16 = 0.125. En cualquier caso, al ser distinto de cero, también es cierto como un booleano, por lo que negarlo dos veces con!!
nuevamente produce1
. Por lo tanto, todo se simplifica a:El operador
>>=
es una asignación compuesta que desplaza su operando izquierdo hacia la derecha por el número de bits dados por el operando derecho y devuelve el resultado. En este caso, el operando correctoa[1]
siempre tiene el valor1
, por lo que es equivalente a:o equivalente:
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 ciclo. Por lo tanto, el cuerpo del bucle se ejecuta tres veces.fuente
P
en0X.1P1
.e
en10e5
, excepto que tiene que usarp
para literales hexadecimales porquee
es un dígito hexadecimal.p
separa la mantisa y el exponente, al igual quee
en la notación científica flotante normal; Una diferencia es que, con flotadores hexagonales, la base de la parte exponencial es 2 en lugar de 10, por lo que0x0.1p1
es igual a 0x0.1 = 1/16 veces 2¹ = 2. (En cualquier caso, nada de eso importa aquí; el valor funcionaría igual de bien allí.)char
literal", y agregué un enlace de Wikipedia. ¡Gracias!Es un código bastante oscuro que involucra dígrafos , 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 desplazamiento leve , la asignación de desplazamiento correcta>>=
.Esta es una versión más legible:
y una versión aún más legible, reemplazando las expresiones en
[]
los valores por los que resuelven:Reemplazar
a[0]
ya[1]
por sus valores debería facilitar la comprensión de lo que está haciendo el ciclo, es decir, el equivalente de:que simplemente realiza la división (entera) por 2 en cada iteración, produciendo la secuencia
5, 2, 1
.fuente
????
, sin embargo, en lugar de lo que???
obtuvo el OP? (Huh.) Codepad.org/nDkxGUNi hace productos???
.Veamos la expresión de izquierda a derecha:
Lo primero que noto es que estamos usando el operador ternario del uso de
?
. Entonces la subexpresión:dice "si
0xFULL
no es cero, return'\0'
, de lo contrario-1
.0xFULL
es un literal hexadecimal con el sufijo largo largo sin signo , lo que significa que es un literal hexadecimal de tipounsigned long long
. Sin embargo, eso realmente no importa, porque0xF
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 aint
, que es justo0
.El valor de
0xF
es mucho mayor que cero, por lo que pasa. La expresión ahora se convierte en:A continuación,
:>
hay un dígrafo . Es una construcción que se expande a]
:>>=
es el operador de desplazamiento a la derecha firmado, podemos espaciarloa
para aclararlo.Además,
<:
es un dígrafo que se expande a[
:0X.1P1
es un literal hexadecimal con un exponente. Pero no importa el valor,!!
todo lo que no sea cero es cierto.0X.1P1
es0.125
que no es cero, por lo que se convierte en:El
>>=
es el operador de desplazamiento 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 es1010
. Así que aquí están los pasos:>>=
devuelve el resultado de su operación, de modo que mientras el desplazamientoa[0]
permanezca distinto de cero por cada vez que sus bits se desplacen uno a la derecha, el bucle continuará. El cuarto intento es donde sea[0]
hace0
, por lo que el ciclo nunca se ingresa.Como resultado,
?
se imprime tres veces.fuente
:>
es un dígrafo , no un trigrafo. No es manejado por el preprocesador, simplemente se reconoce como un token equivalente a]
.?:
) tiene un tipo que es el tipo común de los términos segundo y tercero. El primer término es siempre condicional y tiene un tipobool
. Dado que tanto el segundo como el tercer término tienen tipo,int
el resultado de la operación ternaria seráint
, nounsigned long long
.#
y##
tiene 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