Jak działa kod C, który drukuje od 1 do 1000 bez pętli lub instrukcji warunkowych?

Znalazłem C kod, który drukuje od 1 do 1000 bez pętli i warunkow : Ale nie rozumiem, jak to działa. Czy ktoś może przejrzeć kod i wyjaśnić każdą linijkę?

#include <stdio.h>
#include <stdlib.h>

void main(int j) {
  printf("%d\n", j);
  (&main + (&exit - &main)*(j/1000))(j+1);
}
Author: Community, 2011-10-29

2 answers

Nigdy nie pisz takiego kodu.


Dla j<1000, j/1000 is zero (integer division). Więc:

(&main + (&exit - &main)*(j/1000))(j+1);

Jest równoważne:

(&main + (&exit - &main)*0)(j+1);

Czyli:

(&main)(j+1);

Który wywołuje main z j+1.

If j == 1000, then the same lines comes out as:

(&main + (&exit - &main)*1)(j+1);

Co sprowadza się do

(&exit)(j+1);

Czyli exit(j+1) i opuszcza program.


(&exit)(j+1) i exit(j+1) są zasadniczo tym samym-cytując C99 §6.3.2.1/4:

Wyznacznik funkcji jest wyrażeniem, które ma typ funkcji. Z wyjątkiem sytuacji, gdy jest to operand operatora sizeof lub operatora uniary & , wyznacznik funkcji z type "funkcja zwracająca Typ "jest konwertowana do wyrażenia, które ma typ" wskaźnik do funkcja zwracająca Typ ".

exit jest oznacznikiem funkcji. Nawet bez operatora uniary & address-of, Jest on traktowany jako wskaźnik do funkcji. (The & just makes it wyraźnie.)

I wywołania funkcji są opisane w §6.5.2.2 / 1 i następujące:

Wyrażenie oznaczające wywołaną funkcję mA typ wskaźnik do funkcji zwracający void lub zwracający typ obiektu inny niż typ tablicy.

Więc exit(j+1) Działa ze względu na automatyczną konwersję typu funkcji do typu wskaźnik-funkcja, a (&exit)(j+1) działa również z jawną konwersją do typu wskaźnik-funkcja.

Że jednak powyższy kod nie jest spójny (main przyjmuje dwa argumenty lub żaden w ogóle), a &exit - &main jest, jak sądzę, niezdefiniowany zgodnie z §6.5.6 / 9:

Gdy dwa wskaźniki są odejmowane, oba wskazują na elementy tego samego obiektu array , lub jeden obok ostatniego elementu obiektu array; ...

Dodanie (&main + ...) byłoby ważne samo w sobie i może być użyte, jeśli Dodana ilość była równa zeru, ponieważ §6.5.6/7 mówi:

Do celów tych operatorów, wskaźnik do obiektu, który nie jest elementem tablica zachowuje się tak samo jak wskaźnik do pierwszego elementu tablicy o długości 1 z typ obiektu jako jego Typ elementu.

Więc dodanie zera do &main byłoby ok (ale nie za bardzo użyteczne).

 263
Author: Mat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-11-04 11:27:51

Używa rekurencji, arytmetyki wskaźników i wykorzystuje zaokrąglanie dzielenia liczb całkowitych.

Termin j/1000 zaokrągla się do 0 dla wszystkich j < 1000; gdy j osiągnie 1000, ocenia się do 1.

Teraz jeśli masz a + (b - a) * n, Gdzie n jest 0 LUB 1, kończy się a if n == 0 i b if n == 1. Używając &main (adres main()) i &exit dla a i b, termin (&main + (&exit - &main) * (j/1000)) zwraca &main, gdy j jest poniżej 1000, &exit w przeciwnym razie. Wynikowy wskaźnik funkcji jest następnie podawany argument j+1.

Cała konstrukcja powoduje zachowanie rekurencyjne: podczas gdy j jest poniżej 1000, main wywołuje się rekurencyjnie; gdy j osiągnie 1000, wywołuje exit, powodując zakończenie programu z kodem wyjścia 1001 (który jest trochę brudny, ale działa).

 41
Author: tdammers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-17 13:41:59