Ich habe C
Code gefunden , der von 1 bis 1000 ohne Schleifen oder Bedingungen druckt : Aber ich verstehe nicht, wie es funktioniert. Kann jemand den Code durchgehen und jede Zeile erklären?
#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
quelle
quelle
main
C ++ nicht aufrufen .Antworten:
Schreiben Sie niemals solchen Code.
Für
j<1000
,j/1000
null (Integer - Division). So:ist äquivalent zu:
Welches ist:
Welches ruft
main
mitj+1
.Wenn
j == 1000
, dann kommen die gleichen Zeilen heraus wie:Was darauf hinausläuft
Welches ist
exit(j+1)
und verlässt das Programm.(&exit)(j+1)
undexit(j+1)
sind im Wesentlichen dasselbe - zitiert C99 §6.3.2.1 / 4:exit
ist ein Funktionsbezeichner. Auch ohne das Unäre&
Adresse des Operators wird sie als Zeiger auf die Funktion behandelt. (Das&
macht es nur explizit.)Funktionsaufrufe sind in §6.5.2.2 / 1 und folgenden beschrieben:
So
exit(j+1)
aufgrund der automatischen Konvertierung des Funktionstyps in einen Zeiger-zu-Funktionstyp und(&exit)(j+1)
funktioniert auch mit einer expliziten Konvertierung in einen Zeiger-zu-Funktionstyp.Abgesehen davon ist der obige Code nicht konform (
main
nimmt entweder zwei oder gar keine Argumente an) und&exit - &main
ist meines Erachtens gemäß §6.5.6 / 9 undefiniert:Die Addition
(&main + ...)
wäre an sich gültig und könnte verwendet werden, wenn die hinzugefügte Menge Null wäre, da in §6.5.6 / 7 Folgendes steht:Das Hinzufügen von Null zu
&main
wäre also in Ordnung (aber nicht sehr nützlich).quelle
foo(arg)
und(&foo)(arg)
sind gleichwertig, sie nennen foo mit argument arg. newty.de/fpt/fpt.html ist eine interessante Seite über Funktionszeiger.foo
ist ein Zeiger,&foo
ist die Adresse dieses Zeigers. Im zweiten Fallfoo
handelt es sich um ein Array,&foo
das foo entspricht.((void(*[])()){main, exit})[j / 1000](j + 1);
&foo
ist nicht dasselbe wiefoo
bei einem Array.&foo
ist ein Zeiger auf das Array,foo
ist ein Zeiger auf das erste Element. Sie haben jedoch den gleichen Wert. Für Funktionenfun
und&fun
sind beide Zeiger auf die Funktion.Es verwendet Rekursion, Zeigerarithmetik und nutzt das Rundungsverhalten der Ganzzahldivision.
Der
j/1000
Begriff rundet für alle auf 0 abj < 1000
; Einmalj
1000 erreicht ist, wird 1 ausgewertet.Wenn Sie nun haben
a + (b - a) * n
, won
entweder 0 oder 1 ist, erhalten Siea
ifn == 0
undb
ifn == 1
. Wenn Sie&main
(die Adresse vonmain()
) und&exit
füra
und verwendenb
, wird der Begriff(&main + (&exit - &main) * (j/1000))
zurückgegeben,&main
wenn erj
unter 1000 liegt.&exit
. Dem resultierenden Funktionszeiger wird dann das Argument zugeführtj+1
.Dieses ganze Konstrukt führt zu rekursivem Verhalten: Während
j
es unter 1000 liegt,main
ruft es sich selbst rekursiv auf; Wennj
1000 erreicht ist, wirdexit
stattdessen aufgerufen, wodurch das Programm mit dem Exit-Code 1001 beendet wird (was etwas schmutzig ist, aber funktioniert).quelle
exit
, die den Exit-Code als Argument verwendet und den aktuellen Prozess beendet. Zu diesem Zeitpunkt ist j 1000, also ist j + 1 gleich 1001, was zum Exit-Code wird.