Ist ein main() für ein C-Programm erforderlich?

Lesezeit: 6 Minuten

Benutzeravatar von elricL
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.

  • 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

Benutzeravatar von paxdiablo
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

Nun, nein, aber …

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 Benutzeravatar
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:

  • Viele Plug-In-Systeme für andere Programme (wie zum Beispiel Browser oder Texteditoren oder ähnliches) haben keine main().
  • In C geschriebene Windows-Programme haben keine main(). (Sie haben ein WinMain() stattdessen.)

Benutzeravatar von Clifford
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

Benutzeravatar von Sim Sun
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

Benutzeravatar von agf
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.

1403360cookie-checkIst ein main() für ein C-Programm erforderlich?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy