Nun, der Titel sagt alles. Ist ein main()
Funktion für ein C-Programm unbedingt erforderlich?
Ich frage das, weil ich mir den Linux-Kernel-Code angeschaut habe und keine main()-Funktion gesehen habe.
elricL
Nun, der Titel sagt alles. Ist ein main()
Funktion für ein C-Programm unbedingt erforderlich?
Ich frage das, weil ich mir den Linux-Kernel-Code angeschaut habe und keine main()-Funktion gesehen habe.
paxdiablo
Nein, der ISO-C-Standard besagt, dass a main
Die Funktion ist nur für eine gehostete Umgebung (z. B. eine mit einem zugrunde liegenden Betriebssystem) erforderlich.
Für eine freistehende Umgebung wie ein eingebettetes System (oder ein Betriebssystem selbst) ist die Implementierung definiert. Ab C99 5.1.2
:
Es sind zwei Ausführungsumgebungen definiert: freistehend und gehostet. In beiden Fällen erfolgt der Programmstart, wenn eine bestimmte C-Funktion von der Ausführungsumgebung aufgerufen wird.
In einer freistehenden Umgebung (in der die Ausführung von C-Programmen ohne den Nutzen eines Betriebssystems stattfinden kann) sind Name und Typ der Funktion, die beim Programmstart aufgerufen wird, implementierungsdefiniert.
Wie Linux selbst startet, ist der Ausgangspunkt für den Linux-Kernel start_kernel Für ein vollständigeres Bild des gesamten Startvorgangs sollten Sie jedoch beginnen hier.
Wo immer der Implementierer es haben möchte.
– R.. GitHub HÖR AUF, EIS ZU HELFEN
6. November 2010 um 15:42 Uhr
@Mad-Scientist, siehe das Update bzgl start_kernel
in init/main.c
.
– paxdiablo
6. November 2010 um 15:42 Uhr
Eigentlich verstehe ich die Antwort, nachdem ich es noch einmal gelesen habe. Wenn es sich um ein eingebettetes System handelt, gibt es keinen festen Startpunkt, es kann durch Schalter oder Zurücksetzen gestartet werden. Vielen Dank.
– elricL
6. November 2010 um 15:45 Uhr
Selbst wenn der C-Standard nichts über freistehende Implementierungen aussagte und darauf bestand, dass jedes Programm eine Hauptdatei haben muss, würde der Linux-Kernel dennoch keine benötigen, weil es kein konformes Programm ist. Das heißt, Sie haben keine Gewissheit, dass Sie es auf Ihrem Windows-Rechner mit MSVC kompilieren und korrekt ausführen können – aber Sie haben doch nicht erwartet, dass Sie dazu in der Lage sind, oder? Die meisten Leute hier verstehen den Standard nicht – er macht bestimmte Zusicherungen, wenn Sie bestimmte Dinge tun, aber er kann nicht verhindern, dass Dinge funktionieren, wenn Sie es nicht tun.
– Jim Balter
21. Februar 2011 um 12:02 Uhr
@Mad-scientist Nein, du hast es nicht verstanden. main ist kein “fester Startpunkt”, sondern eine beliebige Stelle im Programm, die vom Linker in der ausführbaren Datei aufgezeichnet wird. Es können aber auch andere Namen gewählt werden, und ein anderer ist für Linux. Und der C-Standard ist hier irrelevant, da der Linux-Kernel kein konformes Programm ist und nicht von einer konformen Implementierung kompiliert wird – schon gar nicht mit den Flags, die zum Erstellen des Linux-Kernels verwendet werden. Einige dumme Leute sagen “aber dann könnte es eine thermonukleare Bombe zünden” – sie verstehen nicht, wie der Standard funktioniert.
– Jim Balter
21. Februar 2011 um 12:34 Uhr
C99 spezifiziert das main()
wird in der gehosteten Umgebung “beim Programmstart” aufgerufen, Sie müssen jedoch nicht die C-Laufzeitunterstützung verwenden. Ihr Betriebssystem führt Bilddateien aus und startet ein Programm an einer vom Linker bereitgestellten Adresse.
Wenn Sie bereit sind, Ihr Programm so zu schreiben, dass es den Anforderungen des Betriebssystems und nicht denen von C99 entspricht, können Sie dies ohne main() tun. Je moderner (und komplexer) das System ist, desto mehr Probleme werden Sie damit haben, dass die C-Bibliothek davon ausgeht, dass der Standard-Runtime-Start verwendet wird.
Hier ein Beispiel für Linux…
$ cat > nomain.S
.text
_start:
call iamnotmain
movl $0xfc, %eax
xorl %ebx, %ebx
int $0x80
.globl _start
$ cat > demo.c
void iamnotmain(void) {
static char s[] = "hello, world\n";
write(1, s, sizeof s);
}
$ as -o nomain.o nomain.S
$ cc -c demo.c
$ ld -static nomain.o demo.o -lc
$ ./a.out
hello, world
Es ist jetzt wohl kein “C99-Programm”, sondern nur ein “Linux-Programm” mit einem in C geschriebenen Objektmodul.
+1. Ich würde still Betrachten Sie das als C99-Programm, nur als eigenständiges. Nichts im Standard besagt, dass Sie haben das OS zu benutzen, nur weil es da ist 🙂
– paxdiablo
8. November 2010 um 0:51 Uhr
Das main()
Die Funktion wird von einer Objektdatei aufgerufen, die in der libc enthalten ist. Da der Kernel nicht mit der libc verlinkt, hat er seinen eigenen Einstiegspunkt, der in Assembler geschrieben ist.
NUR MEINE richtige MEINUNG
Die Antwort von Paxdiablo deckt zwei der Fälle ab, in denen Sie nicht auf eine Hauptleitung stoßen. Lassen Sie mich noch ein paar hinzufügen:
main()
.main()
. (Sie haben ein WinMain()
stattdessen.)
Clifford
Das Betriebssystemladeprogramm muss einen einzigen Einstiegspunkt aufrufen; Im GNU-Compiler ist der Einstiegspunkt in der verknüpften Objektdatei crt0.o definiert, die Quelle dafür ist die Assembler-Datei crt0.s – die main() aufruft, nachdem verschiedene Startaufgaben zur Laufzeit ausgeführt wurden (wie z Stack, statische Initialisierung). Wenn Sie also eine ausführbare Datei erstellen, die das standardmäßige crt0.o verknüpft, müssen Sie ein main() haben, andernfalls erhalten Sie einen Linker-Fehler, da main() in crt0.o ein nicht aufgelöstes Symbol ist.
Es wäre möglich (wenn auch etwas pervers und unnötig), crt0.s zu modifizieren, um einen anderen Einstiegspunkt aufzurufen. Stellen Sie nur sicher, dass Sie eine solche Objektdatei speziell für Ihr Projekt erstellen, anstatt die Standardversion zu ändern, oder Sie werden jeden Build auf diesem Computer beschädigen.
Das Betriebssystem selbst verfügt über ein eigenes C-Runtime-Startup (das vom Bootloader aufgerufen wird), sodass es jeden gewünschten Einstiegspunkt aufrufen kann. Ich habe mir die Linux-Quelle nicht angesehen, aber stellen Sie sich vor, dass sie ihre eigene crt0.s hat, die den Einstiegspunkt des C-Codes aufruft.
Der Loader des Betriebssystems ruft nicht auf main()
.
– NUR MEINE richtige MEINUNG
7. November 2010 um 3:18 Uhr
Sim Sonne
main wird von glibc aufgerufen, das ist ein Teil der Anwendung (Ring 3), nicht der Kernel (Ring 0).
Der Treiber hat einen anderen Einstiegspunkt, zum Beispiel wird die Windows-Treiberbasis auf WDM von DRIVERENTRY gestartet
Der Loader des Betriebssystems ruft nicht auf main()
.
– NUR MEINE richtige MEINUNG
7. November 2010 um 3:18 Uhr
agf
In der Maschinensprache werden die Dinge sequentiell ausgeführt, was zuerst kommt, wird zuerst ausgeführt. Die Standardeinstellung ist also, dass der Compiler Ihre Hauptmethode aufruft, um dem C-Standard zu entsprechen.
Ihr Programm funktioniert wie eine Bibliothek, die eine Sammlung kompilierter Funktionen ist. Der Hauptunterschied zwischen einer Bibliothek und einer ausführbaren Standarddatei besteht darin, dass der Compiler für die zweite Assemblercode generiert, der eine der Funktionen in Ihrem Programm aufruft.
Aber Sie könnten Assembler-Code schreiben, der Ihre beliebige C-Programmfunktion aufruft (genauso wie Aufrufe von Bibliotheksfunktionen tatsächlich funktionieren), und dies würde genauso funktionieren wie andere ausführbare Dateien. Aber die Sache ist, dass Sie es nicht in einfachem Standard-C tun können, Sie müssen auf Assembler oder sogar einige andere Compiler-spezifische Tricks zurückgreifen.
Dies war als allgemeine und oberflächliche Erklärung gedacht, es gibt einige technische Unterschiede, die ich absichtlich vermieden habe, da sie nicht relevant erscheinen.
mögliches Duplikat von Vermeiden des Mains (Einstiegspunkt) in einem C-Programm
– Jens Gustedt
6. November 2010 um 15:39 Uhr
Du übertreibst es wirklich, Jens, wenn du das ein Duplikat nennst.
– NUR MEINE richtige MEINUNG
6. November 2010 um 15:43 Uhr
Ich sehe das selbst nicht als Dupe. Das war eine dieser lästigen Interviewfragen, dies scheint eine ernsthaftere Frage zu sein, wie Linux startet.
– paxdiablo
6. November 2010 um 15:44 Uhr
Nun, ich habe mir gerade die Linux-Quelle angesehen, und die Frage ist aufgetaucht. Ich habe vor dem Posten gesucht und nicht darüber nachgedacht, wie ich main() und die Notwendigkeit von main vermeiden kann, waren die gleiche Frage. Ehrlicher Fehler.
– elricL
6. November 2010 um 16:00 Uhr
Es war überhaupt kein Fehler, @Mad-scientist. Jens springt die Waffe groß Zeit.
– NUR MEINE richtige MEINUNG
7. November 2010 um 3:21 Uhr