Ich habe von meinem Kollegen gelernt, dass man ein C-Programm schreiben und ausführen kann, ohne eine main()
Funktion zu schreiben . Es kann so gemacht werden:
my_main.c
/* Compile this with gcc -nostartfiles */
#include <stdlib.h>
void _start() {
int ret = my_main();
exit(ret);
}
int my_main() {
puts("This is a program without a main() function!");
return 0;
}
Kompilieren Sie es mit diesem Befehl:
gcc -o my_main my_main.c –nostartfiles
Führen Sie es mit diesem Befehl aus:
./my_main
Wann müsste man so etwas tun? Gibt es ein reales Szenario, in dem dies nützlich wäre?
_start()
und andere Dinge außerhalb von besprochen werdenmain()
._start
oder über einen anderen Einstiegspunkt ausmain
(außer dass der Name des Einstiegspunkts für freistehende (eingebettete) Implementierungen implementierungsdefiniert ist).Antworten:
Das Symbol
_start
ist der Einstiegspunkt Ihres Programms. Das heißt, die Adresse dieses Symbols ist die Adresse, zu der beim Programmstart gesprungen wurde. Normalerweise wird die Funktion mit dem Namen_start
von einer aufgerufenen Datei bereitgestellt,crt0.o
die den Startcode für die C-Laufzeitumgebung enthält. Es richtet einige Dinge ein, füllt das Argumentarrayargv
, zählt, wie viele Argumente vorhanden sind, und ruft dann aufmain
. Nachmain
Rückgabeexit
wird aufgerufen.Wenn ein Programm die C-Laufzeitumgebung nicht verwenden möchte, muss es seinen eigenen Code für bereitstellen
_start
. Die Referenzimplementierung der Programmiersprache Go tut dies beispielsweise, weil sie ein nicht standardmäßiges Threading-Modell benötigen, das etwas Magie mit dem Stapel erfordert. Es ist auch nützlich, eigene zu liefern,_start
wenn Sie wirklich kleine Programme oder Programme schreiben möchten, die unkonventionelle Dinge tun.quelle
_start
kommt auch aus der Objektdateicrt0.o
._start
; Tatsächlich wird nicht angegeben, was passiert, bevor überhauptmain
aufgerufen wird, sondern nur, welche Bedingungen erfüllt sein müssen, wennmain
aufgerufen wird. Es ist eher eine Konvention für den Einstiegspunkt_start
, der aus alten Zeiten stammt.Während dies aus
main
Sicht des Programmierers der Einstiegspunkt für Ihr Programm_start
ist , ist dies aus Sicht des Betriebssystems der übliche Einstiegspunkt (die erste Anweisung, die ausgeführt wird, nachdem Ihr Programm vom Betriebssystem aus gestartet wurde).In einem typischen C- und insbesondere C ++ - Programm wurde viel Arbeit geleistet, bevor die Ausführung in main eingeht.
Besonders Dinge wie die Initialisierung globaler Variablen.Hier finden Sie eine gute Erklärung für alles finden , was los ist auf zwischen_start()
undmain()
und auch nach der Haupt hat wieder verlassen (siehe Kommentar unten).Der dafür erforderliche Code wird normalerweise von den Compiler-Autoren in einer Startdatei bereitgestellt, aber mit dem Flag
–nostartfiles
teilen Sie dem Compiler im Wesentlichen mit: "Geben Sie mir nicht die Standard-Startdatei , sondern geben Sie mir die volle Kontrolle darüber, was direkt von der Anfang".Dies ist manchmal notwendig und wird häufig auf eingebetteten Systemen verwendet. Wenn Sie beispielsweise kein Betriebssystem haben und bestimmte Teile Ihres Speichersystems (z. B. Caches) vor der Initialisierung Ihrer globalen Objekte manuell aktivieren müssen.
quelle
_start()
(oder einer anderen von ihm aufgerufenen Funktion) ausgeführt wird, und in vielen Bare-Metal-Programmen kopieren Sie explizit alle globalen Daten von Flash in den RAM Erstens, was auch in passiert_start()
, aber diese Frage betraf weder C ++ noch Bare-Metal-Code._start
die C-Bibliothek nur initialisiert wird, wenn Sie spezielle Schritte ausführen, um dies selbst zu tun. Es kann durchaus unsicher sein, eine nicht asynchronsignalsichere Funktion eines solchen Programms zu verwenden. (Es gibt keine offizielle Garantie dafür, dass eine Bibliotheksfunktion funktioniert, aber asynchronsignalsichere Funktionen können überhaupt nicht auf globale Daten verweisen, sodass sie sich um Fehlfunktionenmalloc
nicht initialisiert werden.errno
(zBread
undwrite
bin Async-Signal-safe und einstellen kannerrno
) und das könnte möglicherweise ein Problem sein , genau je nachdem , wann der pro Threaderrno
Standort zugeordnet ist .Hier ist ein guter Überblick, was beim Programmstart geschieht vor
main
. Insbesondere zeigt es , dass__start
ist der eigentliche Einstiegspunkt zu Ihrem Programm von OS Sicht.Dies ist die allererste Adresse, von der aus der Anweisungszeiger in Ihrem Programm zu zählen beginnt.
Der dortige Code ruft einige Routinen der C-Laufzeitbibliothek auf, um die Verwaltung zu übernehmen. Rufen Sie dann Ihre
main
an und bringen Sie die Dinge herunter und rufen Sieexit
mit dem zurückgegebenen Exit-Code aufmain
.Ein Bild sagt mehr als tausend Worte:
PS: Diese Antwort wurde aus einer anderen Frage übernommen, die SO als Duplikat dieser Frage hilfreich geschlossen hat.
quelle
Wenn Sie Ihren eigenen Startcode für Ihr Programm wünschen.
main
ist nicht der erste Eintrag für ein C-Programm, sondern der erste_start
Eintrag hinter dem Vorhang.Beispiel unter Linux:
Wenn Sie meinen, implementieren Sie unsere eigenen
_start
:Ja, in den meisten kommerziellen Embedded-Programmen, mit denen ich gearbeitet habe, müssen wir unsere eigenen implementieren,
_start
um unsere spezifischen Speicher- und Leistungsanforderungen zu erfüllen.Wenn Sie meinen, lassen Sie die
main
Funktion fallen und ändern Sie sie in etwas anderes:Nein, ich sehe keinen Vorteil darin.
quelle