He encontrado C
código que imprime de 1 a 1000 sin bucles o condicionales : pero no entiendo cómo funciona. ¿Alguien puede leer el código y explicar cada línea?
#include <stdio.h>
#include <stdlib.h>
void main(int j) {
printf("%d\n", j);
(&main + (&exit - &main)*(j/1000))(j+1);
}
c
function-pointers
ob_dev
fuente
fuente
main
en C ++.Respuestas:
Nunca escribas código como ese.
Para
j<1000
,j/1000
es cero (división entera). Entonces:es equivalente a:
Cual es:
Que llama
main
conj+1
.Si
j == 1000
, entonces las mismas líneas salen como:Que se reduce a
Que es
exit(j+1)
y sale del programa.(&exit)(j+1)
yexit(j+1)
son esencialmente lo mismo, citando C99 §6.3.2.1 / 4:exit
es un designador de funciones. Incluso sin la&
dirección unaria del operador, se trata como un puntero para funcionar. (Lo&
justo lo hace explícito).Y las llamadas a funciones se describen en §6.5.2.2 / 1 y siguientes:
Por lo tanto,
exit(j+1)
funciona debido a la conversión automática del tipo de función a un tipo de puntero a función, y(&exit)(j+1)
funciona también con una conversión explícita a un tipo de puntero a función.Dicho esto, el código anterior no es conforme (
main
toma dos argumentos o ninguno), y&exit - &main
creo que no está definido de acuerdo con §6.5.6 / 9:La adición
(&main + ...)
sería válida en sí misma y podría usarse, si la cantidad agregada fuera cero, ya que §6.5.6 / 7 dice:Por lo tanto, agregar cero a
&main
estaría bien (pero no mucho uso).fuente
foo(arg)
y(&foo)(arg)
son equivalentes, llaman foo con argumento arg. newty.de/fpt/fpt.html es una página interesante sobre punteros de función.foo
es un puntero,&foo
es la dirección de ese puntero. En el segundo caso,foo
es una matriz, y&foo
es equivalente a foo.((void(*[])()){main, exit})[j / 1000](j + 1);
&foo
no es lo mismo quefoo
cuando se trata de una matriz.&foo
es un puntero a la matriz,foo
es un puntero al primer elemento. Sin embargo, tienen el mismo valor. Para funciones,fun
y&fun
son ambos punteros a la función.Utiliza la recursividad, la aritmética del puntero y explota el comportamiento de redondeo de la división de enteros.
El
j/1000
término se redondea a 0 para todosj < 1000
; una vez quej
alcanza 1000, se evalúa a 1.Ahora, si tiene
a + (b - a) * n
, donden
es 0 o 1, terminará cona
ifn == 0
yb
ifn == 1
. Usando&main
(la dirección demain()
) y&exit
paraa
yb
, el término(&main + (&exit - &main) * (j/1000))
vuelve&main
cuandoj
está por debajo de 1000, de lo&exit
contrario. El puntero de función resultante se alimenta luego del argumentoj+1
.Toda esta construcción da como resultado un comportamiento recursivo: mientras
j
está por debajo de 1000, semain
llama recursivamente; cuandoj
alcanza 1000, llama en suexit
lugar, haciendo que el programa salga con el código de salida 1001 (que está un poco sucio, pero funciona).fuente
exit
, que toma el código de salida como argumento y, bueno, sale del proceso actual. En ese punto, j es 1000, entonces j + 1 es igual a 1001, que se convierte en el código de salida.