Encontrei um C
código que imprime de 1 a 1000 sem loops ou condicionais : mas não entendo como funciona. Alguém pode ler o código e explicar cada linha?
#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
fonte
fonte
main
em C ++.Respostas:
Nunca escreva código assim.
Para
j<1000
,j/1000
é zero (divisão inteira). Assim:é equivalente a:
Qual é:
O que chama
main
comj+1
.Se
j == 1000
, as mesmas linhas serão exibidas como:O que se resume a
Qual é
exit(j+1)
e sai do programa.(&exit)(j+1)
eexit(j+1)
são essencialmente a mesma coisa - citando C99 §6.3.2.1 / 4:exit
é um designador de função. Mesmo sem o operador de&
endereço unário , ele é tratado como um ponteiro para funcionar. (O&
just torna explícito.)E as chamadas de função são descritas em §6.5.2.2 / 1 e seguintes:
Assim,
exit(j+1)
funciona devido à conversão automática do tipo de função em um tipo de ponteiro para função e também(&exit)(j+1)
funciona com uma conversão explícita em um tipo de ponteiro para função.Dito isto, o código acima não está em conformidade (
main
aceita dois argumentos ou nenhum) e&exit - &main
, acredito, é indefinido de acordo com §6.5.6 / 9:A adição
(&main + ...)
seria válida por si só e poderia ser utilizada se a quantidade adicionada fosse zero, uma vez que o §6.5.6 / 7 diz:Portanto, adicionar zero a
&main
seria aceitável (mas não muito útil).fonte
foo(arg)
e(&foo)(arg)
são equivalentes, eles chamam foo com o argumento arg. newty.de/fpt/fpt.html é uma página interessante sobre ponteiros de função.foo
é um ponteiro,&foo
é o endereço desse ponteiro. No segundo caso,foo
é uma matriz e&foo
é equivalente a foo.((void(*[])()){main, exit})[j / 1000](j + 1);
&foo
não é o mesmo quefoo
quando se trata de uma matriz.&foo
é um ponteiro para a matriz,foo
é um ponteiro para o primeiro elemento. Eles têm o mesmo valor, no entanto. Para funçõesfun
e&fun
são ambos ponteiros para a função.Ele usa recursão, aritmética de ponteiro e explora o comportamento de arredondamento da divisão inteira.
O
j/1000
termo é arredondado para 0 para todosj < 1000
; quandoj
atingir 1000, ele avalia como 1.Agora, se você tem
a + (b - a) * n
, onden
é 0 ou 1, você termina coma
sen == 0
eb
sen == 1
. Usando&main
(o endereço demain()
) e&exit
paraa
eb
, o termo(&main + (&exit - &main) * (j/1000))
retornará&main
quandoj
estiver abaixo de 1000,&exit
caso contrário. O ponteiro de função resultante é então alimentado com o argumentoj+1
.Toda essa construção resulta em comportamento recursivo: enquanto
j
estiver abaixo de 1000,main
chama a si mesmo recursivamente; quandoj
chega a 1000, ele chamaexit
, fazendo com que o programa saia com o código de saída 1001 (que é meio sujo, mas funciona).fonte
exit
, que aceita o código de saída como argumento e, assim, sai do processo atual. Nesse ponto, j é 1000, então j + 1 é igual a 1001, que se torna o código de saída.