c puzzle

Código C: ¿Cómo funcionan estos incluso?



puzzle (4)

char *argv[printf("Hello, world!/n")])

printf() devuelve el número de caracteres impresos.

Asi que

int main(int argc, char *argv[printf("Hello, world!/n")]) {}

es equivalente a

int main(int argc, char *argv[14]) {}

más una llamada a printf() que imprime "Hello World"

Acabo de ver esto here

#include <stdio.h> int main(int argc, char *argv[printf("Hello, world!/n")]) {}

Lo que esto hace es imprimir "¡Hola mundo!"

Pero, ¿qué está pasando aquí realmente?

Lo mejor que puedo adivinar es que se compila y arroja a la parte superior de la pila de ejecución, pero la sintaxis ni siquiera me parece legal ...


El código hace uso de la función de matriz de longitud variable de C99, que le permite declarar matrices cuyo tamaño se conoce solo en tiempo de ejecución. printf devuelve un número entero igual al número de caracteres que se imprimieron realmente, por lo que el código se imprime "¡Hola, mundo!" Primero y usa el valor de retorno como el tamaño de argv . La función main sí no hace nada. La llamada real a printf sí misma va en el código de inicio generado por el compilador, que a su vez llama main .

Edición: Acabo de verificar el desensamblaje del código generado por gcc y parece que la llamada a printf va dentro de main , antes que cualquier otro código.


No soy experto en C, pero parece que los argumentos de la línea de comandos se declaran al mismo tiempo que main .


Si descubro cómo lo analizó el compilador, actualizaré esto, pero al menos no es necesario hacer conjeturas sobre cómo se compiló:

objdump --disassemble /tmp/hello (edited): 080483c4 <main>: 80483c4: 55 push %ebp 80483c5: 89 e5 mov %esp,%ebp 80483c7: 83 e4 f0 and $0xfffffff0,%esp 80483ca: 83 ec 10 sub $0x10,%esp 80483cd: b8 a0 84 04 08 mov $0x80484a0,%eax 80483d2: 89 04 24 mov %eax,(%esp) 80483d5: e8 22 ff ff ff call 80482fc <printf@plt> 80483da: c9 leave 80483db: c3 ret 80483dc: 90 nop 80483dd: 90 nop 80483de: 90 nop 80483df: 90 nop

Dado que los ejecutables de Linux se basan normalmente en 0x8048000, la dirección del argumento para printf está en un desplazamiento de 0x00004a0 desde el inicio del binario:

xxd /tmp/hello | grep 00004a0 00004a0: 4865 6c6c 6f2c 2077 6f72 6c64 210a 0000 Hello, world!...

Por lo tanto, la dirección de la cadena se inserta, y se llama a printf con ese argumento. Nada mágico a ese nivel, por lo que todas las cosas divertidas fueron hechas por gcc.