He encontrado Ccó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

mainen C ++.Respuestas:
Nunca escribas código como ese.
Para
j<1000,j/1000es cero (división entera). Entonces:es equivalente a:
Cual es:
Que llama
mainconj+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:exites 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 (
maintoma dos argumentos o ninguno), y&exit - &maincreo 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
&mainestarí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.fooes un puntero,&fooes la dirección de ese puntero. En el segundo caso,fooes una matriz, y&fooes equivalente a foo.((void(*[])()){main, exit})[j / 1000](j + 1);&foono es lo mismo quefoocuando se trata de una matriz.&fooes un puntero a la matriz,fooes un puntero al primer elemento. Sin embargo, tienen el mismo valor. Para funciones,funy&funson 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/1000término se redondea a 0 para todosj < 1000; una vez quejalcanza 1000, se evalúa a 1.Ahora, si tiene
a + (b - a) * n, dondenes 0 o 1, terminará conaifn == 0ybifn == 1. Usando&main(la dirección demain()) y&exitparaayb, el término(&main + (&exit - &main) * (j/1000))vuelve&maincuandojestá por debajo de 1000, de lo&exitcontrario. El puntero de función resultante se alimenta luego del argumentoj+1.Toda esta construcción da como resultado un comportamiento recursivo: mientras
jestá por debajo de 1000, semainllama recursivamente; cuandojalcanza 1000, llama en suexitlugar, 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.