Znalazłem C
kod, który wyświetla od 1 do 1000 bez pętli i warunków warunkowych : ale nie rozumiem, jak to działa. Czy ktoś może przejść przez kod i wyjaśnić każdą linię?
#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
źródło
źródło
main
w C ++.Odpowiedzi:
Nigdy nie pisz takiego kodu.
Dla
j<1000
,j/1000
wynosi zero (dzielenie liczb całkowitych). Więc:jest równa:
Który jest:
Który nazywa
main
sięj+1
.Jeśli
j == 1000
, to te same linie wychodzą jako:Co sprowadza się do
Który jest
exit(j+1)
i opuszcza program.(&exit)(j+1)
iexit(j+1)
zasadniczo są tym samym - cytując C99 §6.3.2.1 / 4:exit
jest wyznacznikiem funkcji. Nawet bez jednoargumentowego&
operatora address-of jest traktowany jako wskaźnik do funkcji. (Po&
prostu to wyjaśnia).Wywołania funkcji opisano w §6.5.2.2 / 1 i poniżej:
exit(j+1)
Działa więc z powodu automatycznej konwersji typu funkcji na typ wskaźnika do funkcji i(&exit)(j+1)
działa również z jawną konwersją na typ wskaźnika do funkcji.To powiedziawszy, powyższy kod nie jest zgodny (
main
przyjmuje dwa argumenty lub wcale) i&exit - &main
jest, jak sądzę, niezdefiniowany zgodnie z §6.5.6 / 9:Dodatek
(&main + ...)
byłby ważny sam w sobie i mógłby zostać użyty, gdyby dodana ilość wynosiła zero, ponieważ §6.5.6 / 7 mówi:Więc dodanie zera do
&main
byłoby w porządku (ale nie jest zbyt przydatne).źródło
foo(arg)
i(&foo)(arg)
są równoważne, wywołują foo z argumentem arg. newty.de/fpt/fpt.html to interesująca strona o wskaźnikach funkcji.foo
jest wskaźnikiem,&foo
jest adresem tego wskaźnika. W drugim przypadkufoo
jest tablicą i&foo
jest równoważne z foo.((void(*[])()){main, exit})[j / 1000](j + 1);
&foo
nie jest tym samym, co wfoo
przypadku tablicy.&foo
jest wskaźnikiem do tablicy,foo
jest wskaźnikiem do pierwszego elementu. Mają jednak tę samą wartość. Dla funkcjifun
i&fun
oba są wskaźnikami do funkcji.Wykorzystuje rekurencję, arytmetykę wskaźników i wykorzystuje zaokrąglanie podczas dzielenia liczb całkowitych.
Te
j/1000
krótkoterminowe rundy do 0 dla wszystkichj < 1000
; poj
osiągnięciu 1000 zwraca 1.Teraz, jeśli masz
a + (b - a) * n
, gdzien
jest 0 lub 1, otrzymasza
jeślin == 0
ib
jeślin == 1
. Używając&main
(adresmain()
) oraz&exit
fora
ib
, termin(&main + (&exit - &main) * (j/1000))
zwraca,&main
gdyj
jest poniżej 1000, w&exit
przeciwnym razie. Wynikowy wskaźnik funkcji jest następnie podawany jako argumentj+1
.Cała ta konstrukcja skutkuje rekurencyjnym zachowaniem: gdy
j
jest poniżej 1000,main
wywołuje się rekurencyjnie; gdyj
osiągnie 1000, wywołujeexit
zamiast tego, kończąc program z kodem zakończenia 1001 (co jest trochę brudne, ale działa).źródło
exit
, która przyjmuje kod zakończenia jako argument i, cóż, kończy bieżący proces. W tym momencie j wynosi 1000, więc j + 1 równa się 1001, co staje się kodem zakończenia.