Pregunta Obstaculizado C Code Contest 2006. Por favor explique sykes2.c


¿Cómo funciona este programa C?

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

Se compila como está (probado en gcc 4.6.3) Imprime el tiempo cuando se compila. En mi sistema:

    !!  !!!!!!              !!  !!!!!!              !!  !!!!!! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!  !!              !!      !!              !!  !!  !! 
    !!  !!!!!!    !!        !!      !!    !!        !!  !!!!!! 
    !!      !!              !!      !!              !!  !!  !! 
    !!      !!              !!      !!              !!  !!  !! 
    !!  !!!!!!              !!      !!              !!  !!!!!!

Fuente: sykes2 - Un reloj en una línea, sykes2 sugerencias del autor

Algunas sugerencias: No hay advertencias de compilación por defecto. Compilado con -Wall, se emiten las siguientes advertencias:

sykes2.c:1:1: warning: return type defaults to ‘int’ [-Wreturn-type]
sykes2.c: In function ‘main’:
sykes2.c:1:14: warning: value computed is not used [-Wunused-value]
sykes2.c:1:1: warning: implicit declaration of function ‘putchar’ [-Wimplicit-function-declaration]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: suggest parentheses around arithmetic in operand of ‘|’ [-Wparentheses]
sykes2.c:1:1: warning: control reaches end of non-void function [-Wreturn-type]

917
2018-03-13 18:22


origen


Respuestas:


Vamos a desofuscarlo.

Sangrado:

main(_) {
    _^448 && main(-~_);
    putchar(--_%64
        ? 32 | -~7[__TIME__-_/8%8][">'txiZ^(~z?"-48] >> ";;;====~$::199"[_*2&8|_/64]/(_&2?1:8)%8&1
        : 10);
}

Introduciendo variables para desenredar este desastre:

main(int i) {
    if(i^448)
        main(-~i);
    if(--i % 64) {
        char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
        char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    } else {
        putchar(10); // newline
    }
}

Tenga en cuenta que -~i == i+1 debido al complemento a dos. Por lo tanto, tenemos

main(int i) {
    if(i != 448)
        main(i+1);
    i--;
    if(i % 64 == 0) {
        putchar('\n');
    } else {
        char a = -~7[__TIME__-i/8%8][">'txiZ^(~z?"-48];
        char b = a >> ";;;====~$::199"[i*2&8|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    }
}

Ahora, tenga en cuenta que a[b] es lo mismo que b[a]y aplica el -~ == 1+ cambiar de nuevo:

main(int i) {
    if(i != 448)
        main(i+1);
    i--;
    if(i % 64 == 0) {
        putchar('\n');
    } else {
        char a = (">'txiZ^(~z?"-48)[(__TIME__-i/8%8)[7]] + 1;
        char b = a >> ";;;====~$::199"[(i*2&8)|i/64]/(i&2?1:8)%8;
        putchar(32 | (b & 1));
    }
}

Convirtiendo la recursión en un bucle y escabulléndose en un poco más de simplificación:

// please don't pass any command-line arguments
main() {
    int i;
    for(i=447; i>=0; i--) {
        if(i % 64 == 0) {
            putchar('\n');
        } else {
            char t = __TIME__[7 - i/8%8];
            char a = ">'txiZ^(~z?"[t - 48] + 1;
            int shift = ";;;====~$::199"[(i*2&8) | (i/64)];
            if((i & 2) == 0)
                shift /= 8;
            shift = shift % 8;
            char b = a >> shift;
            putchar(32 | (b & 1));
        }
    }
}

Esto produce un personaje por iteración. Cada 64º personaje, genera una nueva línea. De lo contrario, utiliza un par de tablas de datos para averiguar qué mostrar, y pone el carácter 32 (un espacio) o el carácter 33 (un !) La primera mesa (">'txiZ^(~z?") es un conjunto de 10 mapas de bits que describe la apariencia de cada personaje y la segunda tabla (";;;====~$::199") selecciona el bit apropiado para mostrar desde el mapa de bits.

La segunda mesa

Comencemos examinando la segunda tabla, int shift = ";;;====~$::199"[(i*2&8) | (i/64)];. i/64 es el número de línea (6 a 0) y i*2&8 es 8 iff i es 4, 5, 6 o 7 mod 8.

if((i & 2) == 0) shift /= 8; shift = shift % 8 selecciona el dígito octal alto (para i%8 = 0,1,4,5) o el dígito octal bajo (para i%8 = 2,3,6,7) del valor de la tabla. La tabla de cambios termina pareciéndose a esto:

row col val
6   6-7 0
6   4-5 0
6   2-3 5
6   0-1 7
5   6-7 1
5   4-5 7
5   2-3 5
5   0-1 7
4   6-7 1
4   4-5 7
4   2-3 5
4   0-1 7
3   6-7 1
3   4-5 6
3   2-3 5
3   0-1 7
2   6-7 2
2   4-5 7
2   2-3 3
2   0-1 7
1   6-7 2
1   4-5 7
1   2-3 3
1   0-1 7
0   6-7 4
0   4-5 4
0   2-3 3
0   0-1 7

o en forma tabular

00005577
11775577
11775577
11665577
22773377
22773377
44443377

Tenga en cuenta que el autor utilizó el terminador nulo para las dos primeras entradas de la tabla (¡furtivo!).

Esto está diseñado después de una pantalla de siete segmentos, con 7s como espacios en blanco. Entonces, las entradas en la primera tabla deben definir los segmentos que se iluminan.

La primera mesa

__TIME__ es una macro especial definida por el preprocesador. Se expande a una constante de cadena que contiene la hora a la que se ejecutó el preprocesador, en la forma "HH:MM:SS". Observe que contiene exactamente 8 caracteres. Tenga en cuenta que 0-9 tienen valores ASCII 48 a 57 y : tiene ASCII valor 58. El resultado es 64 caracteres por línea, por lo que deja 8 caracteres por carácter de __TIME__.

7 - i/8%8 es por lo tanto el índice de __TIME__ que actualmente se está produciendo (el 7- es necesario porque estamos iterando i hacia abajo). Asi que, t es el personaje de __TIME__ siendo salida.

a termina igualando lo siguiente en binario, dependiendo de la entrada t:

0 00111111
1 00101000
2 01110101
3 01111001
4 01101010
5 01011011
6 01011111
7 00101001
8 01111111
9 01111011
: 01000000

Cada número es un mapa de bits describiendo los segmentos que se iluminan en nuestra pantalla de siete segmentos. Como los caracteres son todos ASCII de 7 bits, el bit alto siempre se borra. Así, 7 en la tabla de segmentos siempre se imprime como un espacio en blanco. La segunda mesa se ve así con el 7s como espacios en blanco:

000055  
11  55  
11  55  
116655  
22  33  
22  33  
444433  

Así por ejemplo, 4 es 01101010 (conjunto de bits 1, 3, 5 y 6), que se imprime como

----!!--
!!--!!--
!!--!!--
!!!!!!--
----!!--
----!!--
----!!--

Para mostrar que entendemos realmente el código, ajustemos la salida un poco con esta tabla:

  00  
11  55
11  55
  66  
22  33
22  33
  44

Esto está codificado como "?;;?==? '::799\x07". Para propósitos artísticos, agregaremos 64 a algunos de los caracteres (ya que solo se usan los 6 bits bajos, esto no afectará la salida); esto da "?{{?}}?gg::799G" (tenga en cuenta que el octavo personaje no está en uso, por lo que podemos hacer lo que queramos). Poniendo nuestra nueva tabla en el código original:

main(_){_^448&&main(-~_);putchar(--_%64?32|-~7[__TIME__-_/8%8][">'txiZ^(~z?"-48]>>"?{{?}}?gg::799G"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}

obtenemos

          !!              !!                              !!   
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
    !!  !!              !!  !!  !!  !!              !!  !!  !! 
          !!      !!              !!      !!                   
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
    !!  !!  !!          !!  !!      !!              !!  !!  !! 
          !!              !!                              !!   

justo como esperábamos No es tan sólido como el original, lo que explica por qué el autor eligió usar la tabla que hizo.


1768
2018-03-13 19:46



Vamos a formatear esto para una lectura más fácil:

main(_){
  _^448&&main(-~_);
  putchar((--_%64) ? (32|-(~7[__TIME__-_/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[_*2&8|_/64]/(_&2?1:8)%8&1):10);
}

Entonces, ejecutarlo sin argumentos, _ (argc convencionalmente) es 1. main() se llamará recursivamente a sí mismo, pasando el resultado de -(~_) (NOT bit a bit negativo de _), así que realmente irán 448 recursiones (solo condición donde _^448 == 0)

Tomando eso, imprimirá 7 líneas de ancho de 64 caracteres (la condición ternaria externa, y 448/64 == 7) Así que vamos a reescribirlo un poco más limpio:

main(int argc) {
  if (argc^448) main(-(~argc));
  if (argc % 64) {
    putchar((32|-(~7[__TIME__-argc/8%8])[">'txiZ^(~z?"-48]>>(";;;====~$::199")[argc*2&8|argc/64]/(argc&2?1:8)%8&1));
  } else putchar('\n');
}

Ahora, 32 es decimal para el espacio ASCII. O bien imprime un espacio o un '!' (33 es '!', De ahí el '&1' al final). Vamos a centrarnos en la burbuja en el medio:

-(~(7[__TIME__-argc/8%8][">'txiZ^(~z?"-48]) >>
     (";;;====~$::199"[argc*2&8|argc/64]) / (argc&2?1:8) % 8

Como dijo otro cartel, __TIME__ es el tiempo de compilación para el programa, y ​​es una cadena, por lo que hay algo de aritmética de cadenas, así como aprovechar que un subíndice de matriz sea bidireccional: a [b] es lo mismo que b [a] para las matrices de caracteres.

7[__TIME__ - (argc/8)%8]

Esto seleccionará uno de los primeros 8 caracteres en __TIME__. Esto luego se indexa en [">'txiZ^(~z?"-48] (0-9 caracteres son 48-57 decimales). Los caracteres en esta cadena deben haber sido elegidos por sus valores ASCII. Este mismo carácter de manipulación de código ASCII continúa a través de la expresión, para dar como resultado la impresión de un '' o '!' dependiendo de la ubicación dentro del glifo del personaje.


97
2018-03-13 21:11



Agregando a las otras soluciones, -~x es igual a x+1 porque ~x es equivalente a (0xffffffff-x). Esto es igual a (-1-x) en complemento a 2s, entonces -~x es -(-1-x) = x+1.


46
2018-03-14 23:54



Desconfuse el módulo de aritmética tanto como pude y eliminé la recursión

int pixelX, line, digit ;
for(line=6; line >= 0; line--){
  for (digit =0; digit<8; digit++){
    for(pixelX=7;pixelX > 0; pixelX--){ 
        putchar(' '| 1 + ">'txiZ^(~z?"["12:34:56"[digit]-'0'] >> 
          (";;;====~$::199"[pixel*2 & 8  | line] / (pixelX&2 ? 1 : 8) ) % 8 & 1);               
    }
  }
  putchar('\n');
}

Expandiéndolo un poco más:

int pixelX, line, digit, shift;
char shiftChar;
for(line=6; line >= 0; line--){
    for (digit =0; digit<8; digit++){
        for(pixelX=7;pixelX >= 0; pixelX--){ 
            shiftChar = ";;;====~$::199"[pixelX*2 & 8 | line];
            if (pixelX & 2)
                shift = shiftChar & 7;
            else
                shift = shiftChar >> 3;     
            putchar(' '| (">'txiZ^(~z?"["12:34:56"[digit]-'0'] + 1) >> shift & 1 );
        }

    }
    putchar('\n');
}

3
2018-04-21 14:48