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.