Wer erhält den von main() zurückgegebenen Wert?

Ich weiß, dass bei Computern der von der main()Funktion zurückgegebene Wert vom Betriebssystem empfangen wird. Aber was passiert in der main()Funktion eines Mikrocontrollers?

Ich verwende immer void main(), wenn ich C für PIC-Mikrocontroller verwende. Bei der Verwendung von C-Compilern für Mikrocontroller spielt es überhaupt keine Rolle. Weil es kein Betriebssystem gibt, das (sagen wir) "main.c" ausführt. Wenn in diesem Mikrocontroller so etwas wie RTOS läuft, dann ist das Betriebssystem "main.c".
Nicht wirklich ein Duplikat, aber zumindest verwandt: electronic.stackexchange.com/q/30830/4950
Wie die Startup-Funktion definiert wird, ist in der Regel nicht Ihre Entscheidung. Die von Ihnen verwendete Umgebung dokumentiert die unterstützten Startfunktionsformulare. Gehostete C-Implementierungen müssen zwei Formen von mainmit zwei unterschiedlichen Signaturen unterstützen, die beide zurückgeben int. Wenn Sie eine freistehende C-Implementierung verwenden, bestimmt diese Implementierung, wie Sie die Startfunktion schreiben sollten. Sie können keine Rückgabefunktion schreiben void, nur weil sie nicht zurückkehrt. Das Verhalten der Nichtrückgabe unterscheidet sich vom Funktionstyp , der die allgemeinen Aufrufkonventionen beeinflusst.

Antworten (5)

Auf einem Mikrocontroller main()wird nicht wirklich erwartet, dass es jemals beendet wird, und das Verhalten, wenn dies der Fall ist, ist nicht definiert – also liegt es an dem, der die C-Laufzeit für den Mikrocontroller geschrieben hat. Ich habe Systeme gesehen, die:

  • Verwenden Sie eine implizite Schleife um main(), sodass sie beim Beenden einfach erneut aufgerufen wird.
  • Haben Sie eine einfache "Jump-to-self"-Schleife (oder eine HALT-Anweisung), die ausgeführt wird, wenn sie main()jemals beendet wird.
  • Führen Sie einfach den Rest des Codespeichers aus, der dem Aufruf von folgt main(). Das nennt man „ins Unkraut laufen“.

Ich habe noch nie einen gesehen, der überhaupt etwas mit dem von zurückgegebenen Wert macht main(). Wenn Ihnen das wirklich wichtig ist, sollten Sie sich den Quellcode für die C-Laufzeitbibliothek Ihres Systems ansehen – und möglicherweise ändern.

Du warst schneller als ich. +1 für Synchronität.
Der C-Standard, main()der einen intRückgabewert definiert, wurde offensichtlich nicht für einen Mikrocontroller ohne Betriebssystem entwickelt. Dies ist also ein nicht spezifiziertes Verhalten und je nach C-Laufzeit kann alles passieren, wie Dave auflistete.
C, das auf einem betriebssystemlosen Mikrocontroller ausgeführt wird, könnte als eigenständige Implementierung betrachtet werden, und der C-Standard erfordert nicht einmal, dass eine eigenständige Umgebung eine hat, geschweige dennmain() , ihren Rückgabewert zu definieren. Das ist Sache des Implementierers.
@ndim - um Haare zu spalten, void main( void )ist implementierungsdefiniertes Verhalten , kein nicht spezifiziertes Verhalten .
@MichaelEdenfield: In der Tat. Allerdings ist der gesamte Code in C in Form von Funktionen definiert, so dass es niemals möglich ist, ein freistehendes System vollständig in C geschrieben zu haben; Es muss mindestens ein bisschen Assemblersprache (oder was auch immer) vorhanden sein, die eine minimale Umgebung einrichtet, damit eine C-Funktion aufgerufen werden kann. Der offensichtlichste Name für diese Funktion ist main().
@ndim Der C-Standard besagt, dass jede Form von main in Ordnung ist. Hast du die Norm gelesen? Noch wichtiger, haben Sie überprüft, in welchem ​​Unterkapitel sich der Abschnitt befindet, der das intFormat angibt main? 5.1.2.2 Gehostete Umgebung.
Zumindest ruft mein Startcode die globalen Destruktoren auf, sodass alles, was in stdout geschrieben wird, immer noch geleert wird.
Um den obigen Kommentar von @Lundin zu erklären: Der C-Standard kennt eine "gehostete Umgebung" (dh das Programm, das ordnungsgemäß auf einem geeigneten Betriebssystem ausgeführt wird) main(...) , in der eine int. Und dann gibt es die "freistehende Umgebung" (dh ein Programm, das im Grunde auf Bare Metal läuft), wo "Name und Typ der beim Start aufgerufenen Funktion implementierungsdefiniert sind". In Ihrer Mikrocontroller-Umgebung ist also alles erlaubt, und Sie werden sich am besten mit einem Disassembler ansehen, was der Startcode, der in der aus Ihrem Quellcode generierten Binärdatei enthalten ist, tatsächlich macht.
@ndim Sogar in einer gehosteten Implementierung (was für die meisten EE-Fragen wahrscheinlich von vornherein irrelevant ist) darf main in implementierungsdefinierten Formularen deklariert werden. Der Standard ist nicht wirklich klar. Details hier
Ich bin auch auf eine HALT-Anweisung nach dem Sprung nach main gestoßen, wenn Sie zurückkehren, friert das Ding ein.
@istepaniuk: Das entspricht meiner zweiten Kugel. Nicht alle Mikrocontroller haben HALT-Befehle.

Ein weit verbreitetes Missverständnis/Mythos ist, dass dies int maindie einzig gültige Form ist, die von der Norm vorgegeben wird. Das ist nicht wahr.

Der C-Standard spricht von zwei Implementierungen: gehostet und freistehend. "Implementierung" bedeutet in diesem Fall Compiler. Gehostete Compiler kompilieren für ein bestimmtes Betriebssystem und freistehende Compiler kompilieren für eine bestimmte Bare-Metal-Anwendung. Eingebettete Systeme sind fast immer freistehende Systeme – auch bei RTOS.

Freistehende Implementierungen können jede Form für verwenden main(), sie müssen nicht einmal eine Funktion namens main haben. Meistens verwenden sie das Formular void main (void), da es keinen Sinn macht, etwas zurückzusenden.

Wichtig dabei ist, dass immer der Compiler über die Form entscheidet main()und nie der Programmierer.

Freistehende Implementierungen, die etwas zurückgeben, main()sind sehr fragwürdig. Sie fragen sich, ob die Leute, die den Compiler erstellt haben, den Standard tatsächlich gelesen haben ...

Details hier .

Der C-Sprachstandard erlaubt die Implementierung definierter Variationen void main( void )und dies ist die übliche Form in eingebetteten Systemen - einfach, weil sie nicht erwartet werden.

Wenn Sie sich das Compiler-Setup ansehen, gibt es normalerweise ein Bootstrap-Code-Snippet, das vom Reset-Vektor aufgerufen wird und einige grundlegende Initialisierungen durchführt (einschließlich z. B. das Kopieren von Initialisierungswerten in Variablen), bevor main() aufgerufen wird.

Dies wird (normalerweise) auch innerhalb einer Endlosschleife sein oder vielleicht einen Reset durchführen, wenn main()zurückgegeben wird

Sie können ohne Bootstrap nicht nach C gelangen. Der Bootstrap-Code, idealerweise in asm geschrieben (wenn Sie in C ein Henne-Ei-Problem haben), bereitet die Umgebung für die Ausführung vor. C richtet einen Stack ein, bereitet .data vor, bereitet .bss vor und ruft dann auf hauptsächlich. Einige Leute denken, Main kehrt nie zurück, aber das Schöne an Baremetal ist, dass alles an Ihnen liegt. Sie können sicherlich in einer MCU vom Hauptbildschirm zurückkehren, z. B. wenn etwas Fatales passiert, oder wenn Ihr Design zu 100% ereignisgesteuert (Interrupt) ist, richten Sie alle Peripheriegeräte und Interrupts ein, kehren Sie dann zurück und lassen Sie Ihren Bootstrap in einer Endlosschleife drehen, was ist schöner als eine Weile (1) fortfahren; im main()-Code.

Viele Leute verwenden vorgefertigte Bibliotheken und Umgebungen, damit sich Baremetal nicht wie Baremetal anfühlt, und obwohl sie dafür verantwortlich sind, was darunter vor sich geht, schauen Sie nicht, um es zu sehen oder zu kontrollieren. Wieder ein Teil der Schönheit von Baremetal.

Was passiert, wenn main zurückkehrt, hängt vom System ab, sei es ein Betriebssystem oder Baremetal auf einer MCU. Ein kluges Design würde es main ermöglichen, in den Bootstrap zurückzukehren und dann abhängig von diesem Design zu wählen, was zu tun ist, zumindest in eine Endlosschleife zu gehen.

main() ist größtenteils nur ein weiterer Funktionsname für den Compiler, einige Toolchains fügen zusätzliches Zeug hinzu, wenn er diese Funktion sieht, also sollten Sie diese Funktion in Baremetal mit Vorsicht verwenden, um übermäßiges Gepäck in einer solchen ressourcenbeschränkten Umgebung zu vermeiden. Es ist nicht unklug, einen anderen C-Einstiegspunkt als den Namen main() zu verwenden, um dieses Problem zu vermeiden, aber das bedeutet normalerweise, die Kontrolle über das Ganze zu übernehmen, anstatt nur ein paar Zeilen in die Sandbox eines anderen hinzuzufügen (schreiben Sie Ihren eigenen Bootstrap und möglicherweise einen Linker Skript und damit möglicherweise auch die Bibliotheken (keine schlechte Idee)).

Sobald Sie übernehmen, kann der Rückgabewert von main sicherlich etwas bedeuten, auch im Fall eines Interrupt-gesteuerten Designs könnte main() einfach Dinge in Bewegung setzen und dann zu einer Endlosschleife zurückkehren oder auf eine Interrupt-Schleife im Bootstrap warten, aber Sie können wählen, dass der Rückgabewert eine Aktion auslöst, wenn er nicht Null ist, und dann das Gate taktet oder alle Peripheriegeräte für einen fehlgeschlagenen Start mit niedriger Leistung zurücksetzt.

Kurze Antwort: Der Rückgabewert geht in den Äther, da Sie im Allgemeinen entweder direkt oder nachdem der Bootstrap angenommen hat, dass etwas Schlimmes passiert ist, in einer Endlosschleife landen und heruntergefahren oder rückgängig gemacht haben, was immer Sie in Ihrem Teil der Anwendung konfiguriert haben. Jede Sandbox, die die Rückkehr von main() nicht zulässt, ist eine Sandbox, die nicht verwendet werden sollte, sie sollte zumindest minimale Unterstützung haben (eine Endlosschleife).

Kürzere Antwort: Wer erhält den Rückgabewert? Sie sind für diese Anwendung und damit für den Bootstrap verantwortlich.

Es hängt (wie in anderen Antworten erwähnt) von Ihrer Toolchain ab, wird aber beispielsweise in GCC mainals andere Funktionen kompiliert, sodass sein Rückgabewert entsprechend den Aufrufkonventionen gespeichert wird (auf ARM verwende ich das Recht, nicht mit GCC, es wird auf R0 gesetzt kurz vor der Rückkehr).

Ich denke, das ist bei AVR-GCC ähnlich, also kann ein benutzerdefiniertes Skript diesen Wert nach der Hauptrückgabe verwenden.

das geht eher am Ziel vorbei
Es betont, dass derjenige, der anruft main, seinen Rückgabewert erhalten kann. Natürlich wird er in 99,9%-Situationen ignoriert, aber answer gibt Aufschluss darüber, wer diesen Rückgabewert erhalten kann.