Hallo zusammen, rein interessehalber, was macht ein IC nach der C-Methode "main"? Ein C-Programm auf einem Betriebssystem ist dann zuende, baut ggf. noch ein paar Speicherkonstrukte ab und gibt den Returnwert zurück. Was macht ein IC normalerweise? Klar, meistens haben wir eine Endlosschleife, in der unser Code läuft, aber trotzdem gibt es doch ein "danach". Und noch was: Was geschieht bei einer Division durch Null, einer Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise (auf einem Rechner mit Betriebssystem) das Programm beendet? Kann man dann ein Reset auslösen und von vorn beginnen? Gibt es Register für sowas? Ich habe keine konkrete Hardware im Sinn, das ist eher eine theoretische Frage. Danke Daniel
:
Bearbeitet durch User
Daniel K. schrieb: > Hallo zusammen, > rein interessehalber, was macht ein IC nach der C-Methode "main"? > Ein C-Programm auf einem Betriebssystem ist dann zuende, baut ggf. noch > ein paar Speicherkonstrukte ab und gibt den Returnwert zurück. > Was macht ein IC normalerweise? Das selbe sofern er das Ende von main() auch wirklich erreicht. Nicht selten bleiben die aber im sogenannten main-loop, einer geplanten Endlosschleife, stecken.
Daniel K. schrieb: > Und noch was: Was geschieht bei einer Division durch Null, einer > Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise > (auf einem Rechner mit Betriebssystem) das Programm beendet? Zu vielfältig hier alles aufzuzählen. Im besten Fall bleibt der IC einfach hängen und macht nicht weiter, im schlimmsten Fall rechnet er mit falschen Werten weiter. Es ist eigentlich immer besser, die Software vor die Wand fahren zu lassen als das Auto, dass der IC steuert. Deshalb muss man sich auch immer gute Testroutinen überlegen und jede Menge Fehler berücksichtigen ;)
Das kommt wohl immer auf die Architektur an. Wenn keine Endlosschleife läuft, dann ist er halt einfach fertig und der Programcounter hat fertig. Die ganzen Registerinhalte bleiben dann halt so und gut ist. Zumindest der AVR hat z.B. keine Probleme mit Division durch 0. Er landet dann in keiner internen Endlosschleife oder so, sondern die Struktur fängt das ab und es kommt nur "not a number" als Ergebnis zurück. Für Fälle, in denen der Controller in einer unbeabsichtigten Schleife hängen bleiben kann, gibt es den Watchdog. Wenn der nicht regelmäßig getreten wird, resettet er den Controller.
Daniel K. schrieb: > Was macht ein IC normalerweise? Der µC bekommt sein Programm vom Compiler/Linker. Guck dir im LST-File an, was der für Maschinencode erzeugt.
Beitrag #5700772 wurde vom Autor gelöscht.
Daniel K. schrieb: > Klar, meistens haben wir eine Endlosschleife, in der unser Code läuft, > aber trotzdem gibt es doch ein "danach". > Und noch was: Was geschieht bei einer Division durch Null, einer > Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise > (auf einem Rechner mit Betriebssystem) das Programm beendet? > Kann man dann ein Reset auslösen und von vorn beginnen? Gibt es Register > für sowas? Alle diese Betriebszustände sollten erkannt, ggf. ein zugehöriger Fehlerstatus gespeichert werden. Oft landet man dann in einer Endlosschleife, aus welcher entweder ein "Watchdog" nach definierter Zeit einen "Reset" auslöst. Oder es wird auf manuelles Abschalten der Versorgungsspannung gewartet (die ggf. auch ausgelöst wurde). Nach Wiederanlauf (über Reset oder nächstem Power-On) kann ggf. der gespeicherte Fehlerstatus ausgewertet und daraufhin bestimmte Zusatzaktivitäten gestartet werden. Manche uC haben Sonderregister, die diese Funktionen unterstützen. Die allermeisten haben den "Watchdog" konfigurierbar integriert. Allereinfachster Fall ist: Warten auf Watchdog-Reset, Neuanlauf. Gruss
my2ct schrieb: > Guck dir im LST-File an Z.B. AVR-GCC:
1 | ba: 04 d0 rcall .+8 ; 0xc4 <main> |
2 | bc: 06 c0 rjmp .+12 ; 0xca <_exit> |
3 | |
4 | 000000c4 <main>: |
5 | int main(void) |
6 | {
|
7 | while (false) |
8 | {
|
9 | }
|
10 | }
|
11 | c4: 80 e0 ldi r24, 0x00 ; 0 |
12 | c6: 90 e0 ldi r25, 0x00 ; 0 |
13 | c8: 08 95 ret |
14 | |
15 | 000000ca <_exit>: |
16 | ca: f8 94 cli |
17 | |
18 | 000000cc <__stop_program>: |
19 | cc: ff cf rjmp .-2 ; 0xcc <__stop_program> |
Was soll er schon machen? Weiterhin das hinterlegte Programm ausführen! Nach Main können lauter NOPs stehen. Dann macht der µC NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP................. ... ... ... ... bis das Ende vom Flash erreicht ist und macht vorne beim Flash weiter. Dort steht dann in der Regel dein Programm oder Bootloader. Wenn du deinen Flash vor dem Programmieren nicht gelöscht hast, kann nach dem Main wirres Zeug stehen, dann wird halt irgendetwas gemacht - aber eben das was da steht!
Daniel K. schrieb: > Was macht ein IC normalerweise? Das, was im Datenblatt steht. Und wenn das IC ein µC ist, dann das, was im Programm steht. "Üblicherweise" bleibt ein µC nach "Beendigung" von main() aber wie beschrieben in einer Endlosschleife stehen. Genaueres steht im Startup-Code. Daniel K. schrieb: > Klar, meistens haben wir eine Endlosschleife, in der unser Code läuft, > aber trotzdem gibt es doch ein "danach". Nein. In einem halbwegs brauchbar designten µC Programm nicht. > Und noch was: Was geschieht bei einer Division durch Null, einer > Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise > (auf einem Rechner mit Betriebssystem) das Programm beendet? Das kommt auf den Prozessor und den dazugehörigen Code an. > Kann man dann ein Reset auslösen und von vorn beginnen? Stichwort dazu: Watchdog
Bimbo. schrieb: > Was soll er schon machen? Weiterhin das hinterlegte Programm ausführen! > Nach Main können lauter NOPs stehen. Dann macht der µC NOP, NOP, NOP, > NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, > NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, > NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, > NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, > NOP, NOP, NOP, NOP Ach so produziert Lego die Noppen für die Bausteine. Jetzt wird mir eniges klar.
Daniel K. schrieb: > rein interessehalber, was macht ein IC nach der C-Methode "main"? Ein IC? Die meisten IC wissen nichts von C oder von einer Funktion namens main(). Wenn du einen µC meinst, dann schreib das auch. > Ein C-Programm auf einem Betriebssystem ist dann zuende, baut ggf. noch > ein paar Speicherkonstrukte ab und gibt den Returnwert zurück. > Was macht ein IC normalerweise? Da es nichts gibt, was mit dem Returnwert irgend etwas anfangen könnten (im Zweifelsfall gibt es keinerlei Ausgabemöglichkeiten, man denke an den µC in der Kaffeemaschine oder im Motorsteuergerät des Autos) ist bei einem µC der Returnwert von main() vollkommen nutzlos. Die meisten C-Compiler erzeugen Code, der main() aufruft und falls der Aufruf zurückkommt, in eine Endlosschleife geht. Beim arm-gcc ist der Startupcode typisch Teil des Projekts. Der könnte dann z.B. so aussehen:
1 | void init() |
2 | {
|
3 | // do global/static data initialization
|
4 | unsigned char *src; |
5 | unsigned char *dest; |
6 | unsigned len; |
7 | src= &INIT_DATA_VALUES; |
8 | dest= &INIT_DATA_START; |
9 | len= &INIT_DATA_END-&INIT_DATA_START; |
10 | while (len--) |
11 | *dest++ = *src++; |
12 | |
13 | // zero out the uninitialized global/static variables
|
14 | dest = &BSS_START; |
15 | len = &BSS_END - &BSS_START; |
16 | while (len--) |
17 | *dest++=0; |
18 | |
19 | main(); |
20 | |
21 | while(1); |
22 | }
|
Hint: weit öfter sieht man diesen Code in Assembler. Das hier habe ich mal irgendwo im Web für "erste Schritte mit dem STM32F030" gefunden. > Und noch was: Was geschieht bei einer Division durch Null, einer > Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise > (auf einem Rechner mit Betriebssystem) das Programm beendet? Falls der in Frage stehende µC so etwas hat, lösen die eine Exception aus. Eine Exception ist im Prinzip wie ein Interrupt, mit einem eigenen Eintrag in der Vektortabelle. Man kann Exceptionhandler im Programm vorsehen, wenn man keine hat, geht das Programm typisch ebenfalls in eine Endlosschleife. Motto: besser gar nichts machen, als etwas falsches.
Lothar M. schrieb: > Und wenn das IC ein µC ist, Axel S. schrieb: > Ein IC? Die meisten IC wissen nichts von C oder von einer Funktion > namens main(). Ich vermute, der TO meint nicht "das IC" (Integrated Circuit), sondern "der IC" (Instruction Counter).
Lothar M. schrieb: > "Üblicherweise" bleibt ein µC nach "Beendigung" von main() aber wie > beschrieben in einer Endlosschleife stehen. Genaueres steht im > Startup-Code. Das muss aber vom Compiler so hinterlegt werden, dass nach Main eine Endlosschleife angelegt wird. Woher soll der Mikrocontroller denn wissen, dass hier das beabsichtige Programm endet? Man muss diesem das irgendwie mitteilen. Ein spezifisches Register gibts hierfür doch nicht. Wenn du in Assembler schreibst (Gott habe den Programmierer seelig), wird nach deinem eigenen "Main" der Controller idR wieder von vorne anfangen, wenn nur NOPs im restlichen Flash liegen.
Axel S. schrieb: > Hint: weit öfter sieht man diesen Code in Assembler. Das hier habe ich > mal irgendwo im Web für "erste Schritte mit dem STM32F030" gefunden. Jetzt habe ich es wiedergefunden: http://eleceng.dit.ie/frank/arm/BareMetalSTM32F0Discovery/blinky.html
Edi R. schrieb: > Ich vermute, der TO meint nicht "das IC" (Integrated Circuit), sondern > "der IC" (Instruction Counter). Dieser Begriff ist für einen PC (Programm Counter) aber äuserst unüblich. Aber weil nach dem Ende von main() sinnvollerweise eine Endlosschleife kommt, springt der IC/PC immer um ein paar Bit hin und her...
Ich meinte einen µC, keinen IC, danke für den Hinweis. Ich habe den Betreff angepasst.
Bimbo. schrieb: > Das muss aber vom Compiler so hinterlegt werden, dass nach Main eine > Endlosschleife angelegt wird. Woher soll der Mikrocontroller denn > wissen, dass hier das beabsichtige Programm endet? Für den µC endet das Programm gar nicht. Das ist ja gerade der Punkt. Der Speicher endet irgendwann mal, das wars aber auch. Und selbst dann springt der PC einfach wieder auf den Anfang des Speichers. Für die Endlosschleife ist bei einem C-Programm der Startupcode verantwortlich. Dessen Quelle hängt von der Toolchain und den verwendeten Libraries ab. Bei AVR ist er Teil der avr-libc. Bei Cortex-M ist er entweder Teil des Programms oder kommt von HAL, SPL oder libopenCM3. Bei manchen Toolchains mag sogar der Compiler das erzeugen. Für Assembler gibt es keinen extra Startupcode. Da muß der Programmierer selber zusehen, wie sein Programm an die richtige Stelle kommt, so daß der µC das Programm nach dem Reset auch ausführt.
Axel S. schrieb: > Für die Endlosschleife ist bei einem C-Programm der Startupcode > verantwortlich. Man sollte auch bedenken, dass Timer und Interrupts nach einem return aus main() durchaus weiterlaufen. Diese werden nämlich in der Regel nicht vom Runttime-System wieder deaktiviert. Wenn das Programm zum Beispiel nur aus ISRs besteht, dann läuft es gar bis zu 100% korrekt weiter. Sonst halt nur zu einem Teil davon (0% - 100%).
Frank M. schrieb: > Wenn das Programm zum Beispiel nur aus ISRs besteht, dann läuft es gar > bis zu 100% korrekt weiter. Beim AVR-GCC nicht, der macht ein CLI nach Main (siehe oben). Ein PWM-Ausgang bleibt aber aktiv.
Frank M. schrieb: > Man sollte auch bedenken, dass Timer und Interrupts nach einem return > aus main() durchaus weiterlaufen. Diese werden nämlich in der Regel > nicht vom Runttime-System wieder deaktiviert. Bei der Kombination AVR + GCC eher nicht. Siehe dazu: Peter D. schrieb: > Z.B. AVR-GCC: ba: 04 d0 rcall .+8 ; 0xc4 <main> > bc: 06 c0 rjmp .+12 ; 0xca <_exit> > > 000000c4 <main>: > int main(void) > { > while (false) > { > } > } > c4: 80 e0 ldi r24, 0x00 ; 0 > c6: 90 e0 ldi r25, 0x00 ; 0 > c8: 08 95 ret > > 000000ca <_exit>: > ca: f8 94 cli > > 000000cc <__stop_program>: > cc: ff cf rjmp .-2 ; 0xcc <__stop_program>
Ich sage es mal so herum, vernünftige Programmierer kehren nicht aus main() zurück. Macht man das, verliert man die Kontrolle über den µC. Es passiert das, was im Startup-Code nach main() steht. Was dort steht ist nur für den Notfall gedacht, damit der µC bei ungewolltem Return aus main() nicht komplett Amok läuft. Man kann sich zwar ansehen was dort im Code steht, doch warum drauf verlassen? Bei der nächsten Compiler-Installation kann sich das heimlich geändert haben. Da programmiert man lieber im eigenen Code was man haben möchte wenn der µC nichts mehr zu tun hat.
>was macht ein IC nach der C-Methode "main"?
Die Computer von Lego Mindstorms kehren nach main() wieder zum Hauptmenü
zurück. Exceptions die nicht gefangen werden, werden auf dem Display
angezeigt. nach einem Tastendruck erscheint wieder das Hauptmenü.
Du siehst, dass es keine allgemein gültige Antwort gibt.
Ein vernünftiger Computer schaltet sich aus, wenn er gar kein Programm
mehr auszuführen hat.
Marten Morten schrieb: > Bei der nächsten Compiler-Installation kann sich das heimlich > geändert haben. Vernünftigerweise ist der Startupcode Teil des Projekts, dann hat man den selbst unter Kontrolle. Zumindest bei µC-Projekten wo man sich auf irgendwelche Tricksereien oder spezielle Verhaltensweisen des Startups verlassen will. Genauso wie das zugehörige Linkerscript und andere hardwarespezifische Sachen. Ich überlass da nichts dem Zufall.
Was macht ein Auto auf der Autobahn, wenn man das Lenkrad losläßt und trotzdem den Fuß auf dem Gas lässt?
Bernd K. schrieb: > Zumindest bei µC-Projekten wo man sich auf irgendwelche Tricksereien > oder spezielle Verhaltensweisen des Startups verlassen will. Genauso wie > das zugehörige Linkerscript Bei ARMs ist das gang und gäbe, weil da jeder sein Süppchen kocht (und auch genügend halbgewalkte Dinge durchs Netz geistern). Beim AVR braucht man das nicht, da gibt es einen Linkerscript mit passendem Startupcode „aus der Dose raus“, wobei auch genügend Spielraum für eigene Modifikationen vorgesehen ist (".noinit"-Variablen, ".initN"-Sektionen).
:
Bearbeitet durch Moderator
Sebastian R. schrieb: > umindest der AVR hat z.B. keine Probleme mit Division durch 0. Er > landet dann in keiner internen Endlosschleife oder so, sondern die > Struktur fängt das ab und es kommt nur "not a number" als Ergebnis > zurück. Wo steht das? Und wie sieht NaN bei Int aus?
Walter T. schrieb: > Sebastian R. schrieb: >> umindest der AVR hat z.B. keine Probleme mit Division durch 0. Er >> landet dann in keiner internen Endlosschleife oder so, sondern die >> Struktur fängt das ab und es kommt nur "not a number" als Ergebnis >> zurück. > > Wo steht das? Und wie sieht NaN bei Int aus? AVR haben einfach keine speziellen Interrupts/Event für solche Fehler.
Bernd K. schrieb: > Vernünftigerweise ist der Startupcode Teil des Projekts Man muß nicht absolut jeden Schrunz selber schreiben. Ich traue den Compilerbauern schon zu, daß sie sich dabei was denken. Wenn ich z.B. beim AVR das externe RAM-Interface einschalten muß, dann plaziere ich das einfach in die .init3-Section. Den Startup-Code fasse ich nicht an.
Mein Wissen ist das, dass der AVR überhaupt keine Division kann, sondern eine Divisionsroutine aus der C-Library nutzt. Und die beim GCC durchaus eine Endlosschleife verursachen kann. Aber ich bin nur Laie und hätte nichts dagegen, wenn ich mich zu meinen Ungusten geirrt hätte.
Also bei meinen Programmen würden nach dem Programmcode im Flash definierte Konstanten ausgeführt. Nicht so eine gute Idee, gibt mit Sicherheit einen Crash des Controllers, mit Pech durch sinnfreie Umprogrammierung von Ports einen Hardwareschaden. AVRs machen meistens einen Reset wenn man den Befehlszeiger ins Nirvana schickt. Entweder direkt ausgelöst durch den CPU-Kern bei illegalen Befehlscodes oder weil der Zeiger überläuft und am Anfang des Flash wieder beim Reset-Vektor landet.
Daniel K. schrieb: > Hallo zusammen, > rein interessehalber, was macht ein IC nach der C-Methode "main"? > Ein C-Programm auf einem Betriebssystem ist dann zuende, baut ggf. noch > ein paar Speicherkonstrukte ab und gibt den Returnwert zurück. > Was macht ein IC normalerweise? > > Klar, meistens haben wir eine Endlosschleife, in der unser Code läuft, > aber trotzdem gibt es doch ein "danach". > Und noch was: Was geschieht bei einer Division durch Null, einer > Nullpointer-Exception oder Out-Of-Memory-Exception, die normalerweise > (auf einem Rechner mit Betriebssystem) das Programm beendet? > Kann man dann ein Reset auslösen und von vorn beginnen? Gibt es Register > für sowas? > > Ich habe keine konkrete Hardware im Sinn, das ist eher eine theoretische > Frage. Alles das kann man nicht allgemein beantworten. Es hängt vom Compiler, dem Startup Code und Runtime sowie natürlich der Architektur ab. Es hängt übrigens auch davon ab WIE main() beendet wird. Neben dem normalen Weg via return kann in C++ eine nicht gefangene Exception dazu führen (siehe set_terminate). Auch auf dem PC passieren noch sehr viele Dinge nach main(). Es werden die per atexit() registrierten Funktionen aufgerufen. Dann werden Destruktoren globaler Objekte aufgerufen, auch in C gibt es mehrere vergleichbare interne Mechanismen die kaum jemand kennt. Auf dem PC würden auch die Standard Streams geflushed und geschlossen. Als letztes wird in der Regel abort() aufgerufen. Die Lektüre der libc ist hier sehr hilfreich. Was macht auf einem Embedded Target Sinn? Wir haben z.B. keine Klicki-Bunti-App in der Cloud sondern unseren Startup Code selbst geschrieben (d.h. compilieren mit GCC und "-nostartfiles" und linken unsere Startup Files und Runtime statt dessen). Wir können deshalb nach main() eigentlich tun und lassen was wir wollen. Endlosschleife oder Tetris starten, alles möglich. Man kann sich überlegen dass ein Reset (spezielles Register, bei ARM Cores z.B. SCB_AIRCR.SYSRESETREQ, od. Reset durch Watchdog Timeout) oft geeigneter ist als eine Endlosschleife und davor das atexit(), Destruktor Handling usw. setzen. Division durch Null. Bei Division durch Null muss man meist zunächst unterscheiden, ob hier CPU oder FPU ursächlich ist. Meistens unterscheidet sich das Verhalten danach. Also ob beispielsweise ein int oder ein float verwendet wird. Dadurch bedingt hat der Compiler entweder CPU oder FPU Instruktionen erzeugt. Eine Division durch Null in der CPU führt in der Regel zu einer Interrupt/Exception, eine Division durch Null in der FPU kann zu einer Interrupt/Exception führen. Wir arbeiten z.B. mit einer MCU wo letzteres manuell aktiviert werden muss. Tut man dies nicht, entsteht nur NaN als Ergebnis. Null-Pointer-Exception. Ist nichts anderes als ein Lesen/Schreiben von Adresse 0. Das kann je nach Target problemlos funktionieren - wenn dort RAM Speicher liegt. Liegt dort nur ROM würde zwar Lesen gehen aber nicht schreiben. Liegt dort kein Speicher geht beides nicht. In der Regel wird man dann beim Lesen/Schreiben eine Execption/Interrupt vom Speicherbus erhalten. NULL hat im Gegensatz zu einer beliebigen Adresse ohne Memory-Mapping lediglich per Definition ein spezielles Verhalten weil es von vielen C Funktionen entsprechend interpretiert wird. Ich fürchte die Antworten auf alle deine Fragen würden ein Buch füllen.
Also, obwohl ich kein "C'ler" bin, würde ich erwarten, dass wenn "Main" abgearbeitet ist, nichts mehr passiert! Es sei denn, es steht da noch weiterer Code. Deshalb ist die Frage für mich sinnlos!! Gruß Rainer
Beitrag #5701386 wurde von einem Moderator gelöscht.
Damit das nicht im Dunklen bleibt: Was macht Arduino nach main() ? Er schaltet sämtliche Interrupts ab, uns geht in eine Endlosschleife.
1 | cli
|
2 | rjmp .-2 |
MfG Willi
Rainer V. schrieb: > würde ich erwarten, dass wenn "Main" > abgearbeitet ist, nichts mehr passiert! Erwartungshaltungen möchten enttäuscht werden. Es ist kein Fehler diese Situation zu untersuchen. z.B. die exit() Anweisung führt genau zu dem Punkt Eigentlich oder üblicher weise werden C Programme so gebaut/konfiguriert, dass die Kontrolle an das BS übergeben wird. Nur, auf den kleinen µC gibt es sowas wie ein BS nicht. Wie so oft in C: Letztendlich ist der Programmierer für das Verhalten zuständig. Oft haben die Compiler/Lib Entwickler vorgesorgt. Aber eben nicht immer. Willi schrieb: > Damit das nicht im Dunklen bleibt: > > Was macht Arduino nach main() ? > Er schaltet sämtliche Interrupts ab, > uns geht in eine Endlosschleife. Das wissen wir schon. Denn das ist das vorbereitete AVR-GCC Standard Verhalten.
Ben B. schrieb: > Entweder direkt ausgelöst durch den CPU-Kern bei illegalen Befehlscodes Es gibt bei einem AVR nichts wie einen Trap, der sowas erreichen könnte. Alle Befehle lassen sich irgendwie abarbeiten, schlimmstenfalls halt als NOP (bspw. beim 0xFFFF des gelöschten Flashs). Arduino Fanboy D. schrieb: > Das wissen wir schon. > Denn das ist das vorbereitete AVR-GCC Standard Verhalten. Genauer gesagt: es ist die Funktion _exit() aus der Bibliothek. Kann man auch durch was eigenes ersetzen. Der Startup-Code ruft ganz standardkonform das Äquivalent eines "exit(main());" auf. Prinzipiell könnte eine eigene _exit()-Funktion damit auch den Rückgabewert von main() auswerten, der in Register r24:r25 übergeben wird.
:
Bearbeitet durch Moderator
Jemand schrieb im Beitrag #5701386:
> Die Realität richtet sich aber nicht nach deinen lustigen Erwartungen
Jau, habe ich verstanden...die üblichen "Blödsinnskommentare"
Nach "Main" ist nix...und wenn doch, dann wars kein Main...oder eben
weiterer Code. Was gibts da denn zu rätseln???
"If I where a bird, I would fly to you!"
Gruß Rainer
Rainer V. schrieb: > Nach "Main" ist nix. Üblicherweise (je nach Linker) steht nach dem Code von main allerlei Bibliothekskrempel, den der Linker noch dazu gepackt hat. Wo auch sonst sollte der hinkommen? Aber main ist eine Funktion und hat daher an ihrem Ende ein return.
:
Bearbeitet durch Moderator
Rainer V. schrieb: > Also, obwohl ich kein "C'ler" bin, würde ich erwarten, dass wenn "Main" > abgearbeitet ist, nichts mehr passiert! Es sei denn, es steht da noch > weiterer Code. Deshalb ist die Frage für mich sinnlos!! Das ist sie keineswegs. Für einen Assemblerprogrammierer (und ich weiß genau, wer sich jetzt angesprochen fühlen wird) ist es normal, jeden Aspekt der Programm- ausführung unter Kontrolle zu haben. Nicht nur, daß er sein Programm an eine feste Adresse im Speicher legt, er kann (und muß!) auch sämtliche Vektortabellen mit korrekten, absoluten(!) Adressen füllen, damit der Prozessor nach dem Reset, einem Interrupt, einer Exception ... genau zur richtigen Stelle springt. Für diesen Assemblerprogrammierer ist auch klar, daß (s)ein Programm kein Ende im herkömmlichen Sinn hat. Ein Prozessor kann nicht "nichts" machen. Er kann eine sleep Instruktion ausführen. Oder er kann eine Endlosschleife "ab"arbeiten. Aber es gibt nichts "vor" der Ausführung seines Programms und nichts "danach". Sobald und solange die Betriebsspannung anliegt, arbeitet der Prozessor (s)ein Programm ab. Alles ist Programm. Für einen C-Programmierer sieht die Welt anders aus. Für ihn findet der Urknall - der Anfang der Zeit - mit den Eintritt in die main() Funktion statt. Was davor kommt, oder danach - davon weiß er nichts. So wie für die Physik die Zeit mit dem Urknall beginnt, so beginnt für den C-Programmierer die Zeit mit der ersten Anweisung in main(). Andererseits weiß er aber auch, daß davor noch etwas gewesen sein muß. Es gibt globale Variablen, die zu diesem Zeitpunkt bereits existieren. Die bereits mit ihren Defaultwerten initialisiert worden sind. Es muß also vor dem Anfang der Zeit schon etwas gewesen sein. Und das ist die Laufzeitumgebung von C. Der Teil, der vor main() abläuft, heißt Startup-Code. Und wenn etwas vor main() ist, dann ist sicher auch etwas danach. main() ist wie die Scheibe Salami in einem Sandwich. Allerdings kann man annehmen, daß die Leute, die den Startup-Code hinbekommen haben, es auch hinbekommen, den Code nach main() so zu gestalten, daß er etwas sinnvolles tut. Und in 99.9% der Fälle ist das sinnvollste, nach der Auführung von main() einfach gar nichts zu tun. Eine Endlosschleife ist die kanonische Lösung. Und deswegen findet man sie auch normalerweise an dieser Stelle.
Unglaublich, was man hier noch alles "lernen" kann (...) MAIN ist der Startpunkt der Hauptschleife, Punkt!
Das Verhalten bei Controllern ohne Betriebssystem hängt vom Compiler ab: Bei den 8bit-PICs ließ der alte C18 das Programm auf der Stelle hüpfen, indem er ein
1 | while(1); |
einfügte. Der neuere XC8 erzeugt ein Reset und startet das Programm neu. Allgemein gilt jedoch: Wenn das Programm aus der main()-Routine herausläuft, hat der Programmierer einen Fehler gemacht.
Beitrag #5701585 wurde von einem Moderator gelöscht.
Zwischenzeitlich ist die Originalfrage verschwunden: hmmmm schrieb im Beitrag #5701585: > Kann mal jemand probieren, was passiert, wenn die CPU am Ende ankommt? > Also quasi main() und die while(1) raushauen. > Bleibt der PC dann wirklich stehen? Was soll schon passieren? Die CPU läuft erst einmal weiter. Sie ließt aus dem Flash die nächsten Werte auf die der PC (Program Counter) zeigt, interpretiert diese als Befehl und führt den Befehl aus. Wenn an der Stelle zufällig Daten stehen interpretierst sie die Daten fälschlicherweise als Befehle. Wenn an der Stelle zufällig Code, z.B. einer Funktion steht, arbeitet sie das Stück Programm fälschlicherweise ab. Das macht die CPU so lange, bis eines der folgenden Dinge passiert: * Sie stößt zufällig auf einen Wert im Speicher, den sie als Befehl zum Anhalten der CPU in irgendeiner Form interpretiert. Manche CPUs haben solche Befehle. Daher sind Behauptungen wie eine CPU läuft immer endlos falsch. * Sie stößt zufällig auf einen Wert im Speicher, den sie als Befehl interpretiert, der aber kein gültiger Befehl ist und stürzt dabei ab, hängt sich intern auf. Sie verbleibt dann in einem undefinierten Zustand. Das sollte bei keiner CPU passieren, ist bei moderneren CPUs auch selten, aber prinzipiell nicht unmöglich. * Sie stoßt auf einen Wert im Speicher der einen CPU-Reset auslöst. Das kann ein Reset-Befehl sein oder ein Befehl der einen Fehler auslöst, den die CPU mit einem Reset behandelt. Das Programm beginnt wieder zu laufen. Wenn die CPU die Eigenschaft hat, bei jeder Art von Reset alle Peripherie komplett neu zu initialisieren, dann läuft das Programm eventuell sogar formal richtig. Ein richtig laufendes Programm heißt nicht notwendigerweise, dass es gut ist, dass das Programm anläuft. Zum Beispiel wenn es eine Maschine steuert die besser aus sein sollte. * Externe Signale, wie ein Watchdog-Interrupt, setzen die CPU zurück. * Externe Signale, wie ein Interrupt, führen zur zwischenzeitlichen Ausführung eines Interrupt-Handlers, danach springt die CPU wieder zurück von wo sie in den Interrupt eingesprungen ist und macht weiter Blödsinn. Im Interrupt-Händler kann sie auch Blödsinn machen, da eventuell globale Variablen, die der Händler braucht, mit falschen Werten belegt sind oder der Status von I/O-Registern nicht mehr stimmt. Passiert nichts was die CPU wie oben beschrieben unterbricht, so läuft sie endlos weiter und arbeitet die zufällige gefundenen Informationen im Speicher als Befehle ab. Dabei besteht eine gewisse Wahrscheinlichkeit, dass sie irgendwo in eine Endlosschleife gerät. Wie sieht es in der Praxis aus, wenn ein Steuersystem wild läuft und Blödsinn macht? Auf diesem Flug https://www.youtube.com/watch?v=BoZRhk0W1j4 wurden die Passagiere fast zwei Stunden mit dieser Show gequält. Ein Passagier zeigt wie seine Herzfrequenz steigt. Gut dass es nur das Licht- und Soundsystem war. Wenn es ein System zur Steuerung des Flugzeugs gewesen wäre -> ???
:
Bearbeitet durch User
Jörg W. schrieb: > Aber main ist eine Funktion und hat daher an ihrem Ende ein return. Öhm...nö. Eine
1 | void main(void){ |
2 | ...
|
3 | }
|
hat z.B. an ihrem Ende kein return und wenn doch sollte jeder halbwegs gescheite Compiler der letzten 30 Jahre eine Schimpftriade auf den Programmierer loslassen. Ich finde ja, eines zeigt dieser Thread ganz gut: Einigen ist offensichtlich nicht klar, wie der Programcounter funktioniert.
M. K. schrieb: > Jörg W. schrieb: >> Aber main ist eine Funktion und hat daher an ihrem Ende ein return. > > Öhm...nö. Eine > >
1 | void main(void){ |
2 | > ... |
3 | > } |
4 | >
|
> > hat z.B. an ihrem Ende kein return und wenn doch sollte jeder halbwegs > gescheite Compiler der letzten 30 Jahre eine Schimpftriade auf den > Programmierer loslassen. > ... Öhm, doch. Das haben wir vor Kurzem geklärt. Bei einem standardkonformen Compiler/Laufzeitumgebung steht am Ende von main() ein return 0; Auch wenn der Programmierer das gar nicht hingeschrieben hat.
:
Bearbeitet durch User
M. K. schrieb: > Öhm...nö. Eine > void main(void){ > ... > } > > hat z.B. an ihrem Ende kein return Dann schau mal in das Listfile mit der Assemblerausgabe. Im Initcode steht sowas:
1 | 98 004F |
2 | 99 004F CD 00 00 call _main ;Call user program |
3 | 100 0052 |
Und Deine main wird so übersetzt:
1 | 20 0000 ._main |
2 | 21 0000 C9 ret |
3 | 22 0001 |
Rainer V. schrieb: > Also, obwohl ich kein "C'ler" bin, würde ich erwarten, dass wenn "Main" > abgearbeitet ist, nichts mehr passiert! Es sei denn, es steht da noch > weiterer Code. Deshalb ist die Frage für mich sinnlos!! Was genau ist Deiner Meinung nach "nichts"? An den Datenleitungen liegen elektrische Spannungen, die logische Pegel repräsentieren. Diese können Null oder Eins sein, andere Zustände gibt es nicht. Jeder Null-und-Eins-Kombination ist ein (gültiger oder ungültiger) Maschinenbefehl eindeutig zugeordnet. Und exakt dieser wird ausgeführt.
Daniel K. schrieb: > Ich habe keine konkrete Hardware im Sinn, das ist eher eine theoretische > Frage. Dann lasse solche Fragen ganz einfach bleiben, denn es gibt dafür keine generellen Antworten, auch keine theoretischen. Stattdessen hängt es vom jeweiligen Startupcode ab, was da anschließend passiert oder nicht. Markus F. schrieb: > Das haben wir vor Kurzem geklärt. > Bei einem standardkonformen Compiler/Laufzeitumgebung steht am Ende von > main() ein return 0; Auch du theoretisierts bloß herum. Ich hab schon einige Startupcodes gesehen, wo ein schlichtes LDR R0, =main BX R0 drin stand. Bei sowas ist es schlichtweg VERBOTEN, aus main zurückzukehren, da ist auch ein return 0 genauso verboten, denn es gibt keine Rückkehr. Von wegen über Standardkonformität zu theoretisieren. Und wenn der Compiler meckert, weil ihm bei einem .. main (..) { ... while(...) { ...} } das return fehlt, dann macht man das eben mit einem harten goto .. main (..) { ... immerzu: ... goto immerzu; } Bei sowas kapiert auch der dümmste Compiler, daß es da niemals zurückgeht und er sich das return 0 schenken kann. W.S.
Markus F. schrieb: > Öhm, doch. > > Das haben wir vor Kurzem geklärt. > Bei einem standardkonformen Compiler/Laufzeitumgebung steht am Ende von > main() ein return 0; > > Auch wenn der Programmierer das gar nicht hingeschrieben hat. Nicht nur bei main() (gcc)
1 | int funktionOhneReturn() |
2 | {
|
3 | |
4 | }
|
Wirft eine Warnung:
> warning: no return statement in function returning non-void
Und wenn man den Returnwert nutzt/auswertet, wird man eine Null
erhalten. Zuverlässig.
Wie/Ob man dieses "Feature" sinnvoll nutzen könnte, mag mir noch nicht
einleuchten.
W.S. schrieb: > Markus F. schrieb: >> Das haben wir vor Kurzem geklärt. >> Bei einem standardkonformen Compiler/Laufzeitumgebung steht am Ende von >> main() ein return 0; > > Auch du theoretisierts bloß herum. Ich "theoretisiere nicht herum", ich zitiere aus dem C99-Standard. Und ich benutze (mindestens) C99-kompatible Compiler die das auch so machen (alles andere würde ich nur mit der Kneifzange anfassen). Im Übrigen hat das "ob oder ob nicht" mit dem Startupcode nicht das Geringste zu tun. Wie soll der beeinflussen, was innerhalb main() passiert?
Arduino Fanboy D. schrieb: > Wirft eine Warnung: >> warning: no return statement in function returning non-void > Bei main() nicht. > Und wenn man den Returnwert nutzt/auswertet, wird man eine Null > erhalten. Zuverlässig. mit Ausnahme von main(): UB. Arduino Fanboy D. schrieb: > Und wenn man den Returnwert nutzt/auswertet, wird man eine Null > erhalten. Zuverlässig. P.S.: das "zuverlässig" ist ziemlich unzuverlässig:
1 | int tst(void) |
2 | {
|
3 | volatile int i = 2; |
4 | |
5 | /* return i; */
|
6 | }
|
Mit einem (ziemlich aktuellen) ColdFire-Compiler übersetzt:
1 | Disassembly of section .text: |
2 | |
3 | 00000000 <tst>: |
4 | 0: 598f subql #4,%sp |
5 | 2: 7002 moveq #2,%d0 |
6 | 4: 2e80 movel %d0,%sp@ |
7 | 6: 588f addql #4,%sp |
8 | 8: 4e75 rts |
Hier ist der "Returnwert" (m68k-ABI hat den in D0) ziemlich "zuverlässig" 2.
:
Bearbeitet durch User
soul e. schrieb: > Was genau ist Deiner Meinung nach "nichts"? Sorry, ich habe mich da zu lax ausgedrückt...durch die Art der Fragestellung etwas irritiert, habe ich angenommen, dass "main" eine Endlosschleife bildet, die natürlich durch alles Mögliche unterbrochen werden kann, aber doch "bei sich bei" bleibt. In diesem Sinne passiert nach "main" nichts, weil es kein "nach" gibt! Die Idee, ein (Unter-)Programm zu schreiben, an das sich kein weiterer Code anschließt, die CPU also ins Leere laufen läßt, halte ich für mehr als abstrus. Und die Frage, ob oder was da der Compiler noch dran drehen könnte, ist genau so dümmlich... Gruß Rainer
Es ist doch ganz einfach... Return Werte werden bei den allermeisten Compilern in einem Register zurück gegeben. Wenn also return xxx; im code fehlt seit ein Aufrufer einfach den letzten zufälligen Wert der im Register steht. Deshalb ist es sinnvoll zumindest eine Warnung auszugeben. Thomas
> Was macht ein µC nach main?
Er schwimmt weiter in den Rhein, dann Main ist nur der längste
Nebenfluss des Rheins.
Daniel K. schrieb: > Ich habe keine konkrete Hardware im Sinn, das ist eher eine theoretische > Frage. Dann ist die Antwort einfach: Das Verhalten ist unbestimmt. Du musst für eine konkrete Antwort nicht nur die Hardware im Auge haben, sondern auch die Software wie C-Compiler und LibC. Bei µCs verwendet man mitunter auch selbstgeschriebenen Startup Code, der dann oft den Return aus main() gar nicht erst behandelt.
Rainer V. schrieb: > soul e. schrieb: >> Was genau ist Deiner Meinung nach "nichts"? > > Sorry, ich habe mich da zu lax ausgedrückt...durch die Art der > Fragestellung etwas irritiert, habe ich angenommen, dass "main" eine > Endlosschleife bildet, die natürlich durch alles Mögliche unterbrochen > werden kann, aber doch "bei sich bei" bleibt. In diesem Sinne passiert > nach "main" nichts, weil es kein "nach" gibt! Es ist allgemeiner Konsens, daß auf einem µC eine main() Funktion, die ein return Statement enthält oder ihr Ende erreichen kann, relativ sinnlos ist. Nichtsdestotrotz ist main() erstmal nur eine C-Funktion und als solche kann sie im Prinzip beendet werden. Bei Programmen, die in einem hosted Environment (z.B. auf deinem PC) laufen, ist das sogar der Normalfall. > Die Idee, ein > (Unter-)Programm zu schreiben, an das sich kein weiterer Code > anschließt, die CPU also ins Leere laufen läßt Die CPU läuft aber nicht "ins Leere". main() ruft sich schließlich nicht von alleine auf. Irgendwelcher Code muß den Aufruf von main() enthalten. Und genau dieser Code bestimmt auch, was passiert wenn main() zurückkehrt. Und wie bereits mehrfach gesagt, heißt dieser Code "Startup Code" oder auch "C runtime" und ist in den meisten Fällen Teil der Toolchain. Siehe auch: https://en.wikipedia.org/wiki/Crt0
Jörg W. schrieb: > Bernd K. schrieb: >> Zumindest bei µC-Projekten wo man sich auf irgendwelche Tricksereien >> oder spezielle Verhaltensweisen des Startups verlassen will. Genauso wie >> das zugehörige Linkerscript > > Bei ARMs ist das gang und gäbe, weil da jeder sein Süppchen kocht (und > auch genügend halbgewalkte Dinge durchs Netz geistern). > > Beim AVR braucht man das nicht, da gibt es einen Linkerscript mit > passendem Startupcode „aus der Dose raus“ Ist auch verständlich. Bei AVR kommt alles was die Toolchain bedient von ein und dem selben Hersteller, andere Chips die die selbe Toolchain bräuchten wird es niemals geben, da konnte Atmel hergehen und das alles direkt mit der Toolchain zusammen bundeln und ausliefern. Aber bei ARM gibt es zig Hersteller und von jedem nochmal je zwei Dutzend grundverschiedene Chips. Solange die sich nicht alle aufraffen können friedlich zusammenzuarbeiten und gemeinsam ein Repository zu pflegen wo alle Startups und Linkerscripte (und bei der Gelegenheit auch gleich alle Registerdefinitionen) für alle Cortexe die es gibt auf der Welt an zentraler Stelle gepflegt werden (träum...) wird das niemals Bestandteil von gcc-arm-embedded werden. Deshalb liefert jeder Hersteller in seiner eigenen Distribution der Toolchain noch seine eigenen Templates und Codegeneratoren mit die für seine Chips seinen Startup ausspucken so wie er ihn will. Aber wenn der separat mitgelieferte oder generierte und ausgespuckte Startup dann jedesmal offen vor einem liegt (und man ihn auch ändern oder beliebig ersetzen könnte wenn man wollte) hat das auch den Vorteil daß man Fragen wie in diesem Thread gar nicht stellen müsste, man würde direkt und ohne langes Herumsuchen sofort sehen was bei einem konkret passiert vom ersten bis zum letzten Taktzyklus und man würde sofort sehen wer schuld hat (Hint: nicht "der Compiler"®) wenn es komische Dinge tut oder nicht tut. Es hat also auch Vorteile wenn es so ist wie es bei ARM momentan üblich ist, das ist irgendwie transparenter und expliziter als der verschachtelte Hokuspokus hinter den Kulissen bei AVR.
:
Bearbeitet durch User
Bernd K. schrieb: > Bei AVR kommt alles was die Toolchain bedient von ein und dem selben > Hersteller, andere Chips die die selbe Toolchain bräuchten wird es > niemals geben, da konnte Atmel hergehen und das alles direkt mit der > Toolchain zusammen bundeln und ausliefern. Atmel hat daran schlicht und ergreifend keinen Handschlag gemacht, zumindest nicht zu dem Zeitpunkt, als solche Konzepte implementiert worden sind. (Später haben sie sich teilweise mit in die Entwicklung der Toolchain eingebracht.) Im Gegensatz dazu hatte ARM seine Finger schon sehr lange in der GCC-Entwicklung mit drin, sie hätten es als OEM-übergreifender Hersteller also durchaus in der Hand gehabt, da eine gemeinsame Strategie für alle zu implementieren, wie man Startup-Code und Linkerscripte mit integriert. War ihnen aber offenbar genauso wenig wichtig, wie der GCC lange Zeit Atmel für den AVR unwichtig war (sie hatten ja ihre Kooperation mit IAR). Dass alle ARM-OEMs selbst daran kein großes Interesse haben, ist wiederum verständlich. Es hätte schon ARM als Lizenzgeber selbst in die Hand nehmen müssen.
Jörg W. schrieb: > sie hätten es als OEM-übergreifender > Hersteller also durchaus in der Hand gehabt.. Jörg, vergiß bitte nicht, daß gerade Arm ein vitales Interesse daran hat, daß gerade der GCC ein Stück schlechter gehalten ist als deren eigene Tools. Ich hab das ja ausreichend ausprobiert mit Yagarto versus Keil. Das ist so ähnlich wie das Verhältnis Wolfsburg versus Skoda. Aber für die anfängliche und strunzalberne Frage im Eröffnungspost ist das alles völlig nebensächlich. Ich bin auch nicht dafür, mir eine totale Vereinheitlichung zu wünschen. Gilt für alles, schließlich haben wir in der Geschichte schon genug Versuche zur totalen Vereinheitlichung gehabt. Nee, nicht mit mir. Und.. Jörg W. schrieb: > Es hätte schon ARM als Lizenzgeber selbst in die > Hand nehmen müssen. Das haben sie. Arm hat Keil aufgekauft und man kann die Früchte in Form von ADS, MDK und neuerdings auch eine LLVM-Ablegerversion kriegen. Gegen Geld natürlich. Ja, Geld - das ist in der Industrie die Normalität. Und Steuern sollen ja auch dabei herauskommen. W.S.
W.S. schrieb: > Yagarto Ein inoffizieller Fork von gcc von vor 10 Jahren? Es gibt jetzt einen offiziellen arm-gcc von ARM persönlich betreut und gehostet der eng mit upstream gcc zusammenarbeitet.
:
Bearbeitet durch User
Axel S. schrieb: > Es ist allgemeiner Konsens, daß auf einem µC eine main() Funktion, die > ein return Statement enthält oder ihr Ende erreichen kann, relativ > sinnlos ist. Wie kommst Du denn auf dieses schmale Brett. Es sind durchaus Anwendungen denkbar, die nach getaner Arbeit terminieren. Z.B. eine simple Einschaltverzögerung. Gegenüber einem 555 mit Elko hat ein µC den Vorteil, daß man die Zeit einfach im Sourcecode in Klartext einsetzen kann, daß er ein funktionierendes Poweron-Reset hat und daß er erheblich genauer ist, als ein Elko mit -20/+50% Toleranz. Der C99 Standard schreibt vor, daß implizit ein "return 0;" zu erfolgen hat, sobald der Compiler erkennt, daß das Main terminiert. Es gibt also keinen Grund, das auf einem µC nicht zu tun. Im Gegenteil, ein Standard konformer Compiler ist immer zu bevorzugen.
W.S. schrieb: > örg, vergiß bitte nicht, daß gerade Arm ein vitales Interesse daran hat, > daß gerade der GCC ein Stück schlechter gehalten ist als deren eigene > Tools. Dem widerspricht ihre eigene Mitarbeit bei GCC. Kann aber sein, dass sie da auch erst später eingestiegen sind (Atmel beim AVR aber noch weniger). Keil ist ja auch nur'n Zukauf, war also (im Gegensatz zur IAR-Kooperation bei Atmel's AVR) nicht von vornherein gesetzt. > Aber für die anfängliche und strunzalberne Frage im Eröffnungspost ist > das alles völlig nebensächlich. Korrekt.
Peter D. schrieb: > Es gibt also keinen Grund, das auf einem µC nicht zu tun. Doch: in deinem Anwendungsfall würde man wohl eher die Interrupts sperren und den Prozessor in den Sleep schicken, um sinnloses Energieverplempern zu vermeiden. Dann passiert ab diesem Punkt bis zum nächsten Reset wirklich rein gar nichts. ;-) In der Tat hatte ich so einen Anwendungsfall schon mal in den Fingern: ein kleiner PIC, der einen PLL-IC (in einem Wettersatellitenempfänger) beim Einschalten in Abhängigkeit von ein paar DIP-Schaltern konfigurieren musste und danach nichts mehr zu tun hatte.
Peter D. schrieb: > Axel S. schrieb: >> Es ist allgemeiner Konsens, daß auf einem µC eine main() Funktion, die >> ein return Statement enthält oder ihr Ende erreichen kann, relativ >> sinnlos ist. > > Wie kommst Du denn auf dieses schmale Brett. Das ist es nicht. > Es sind durchaus > Anwendungen denkbar, die nach getaner Arbeit terminieren. > Z.B. eine simple Einschaltverzögerung. Da ist kein Widerspruch. "Arbeit einstellen" ist nicht notwendig das gleiche wie "aus main() zurückkehren". Ein halbwegs erfahrener Programmierer würde den Kern dann in eine Endlosschleife schicken. Oder - besser - einen Sleep-Mode nutzen, um Strom zu sparen. > Der C99 Standard schreibt vor, daß implizit ein "return 0;" zu erfolgen > hat, sobald der Compiler erkennt, daß das Main terminiert. Was hat das mit der Frage zu tun? Der Rückgabewert von main() ist doch vollkommen unerheblich. Wenn man nicht gerade einen selber geschriebenen Startup-Code verwendet, der den auswertet, dann wird der einfach nur weggeworfen. Und es bleibt bei fremdem Startup-Code die Frage, was der als nächstes macht. Irgendwo weiter oben im Thread wollte jemand beobachtet haben, daß sein µC nach dem Ende von main() wieder von vorn anfängt - wie nach einem Reset. Und das will man nun ganz sicher nicht haben, gerade in der von dir skizzierten Anwendung. Dann doch besser eine eigene Endlosschleife am Ende von main().
Axel S. schrieb: > Irgendwo weiter oben im Thread wollte jemand beobachtet haben, daß sein > µC nach dem Ende von main() wieder von vorn anfängt - wie nach einem > Reset. Das würde mich auch mal interessieren, welcher Compiler das sein soll. Typisch steht Main nicht am Ende des Flash, d.h. man würde danach in eine Unterfunktion hinein laufen, was einen Stackunterlauf bewirkt.
Peter D. schrieb: > Axel S. schrieb: >> Irgendwo weiter oben im Thread wollte jemand beobachtet haben, daß sein >> µC nach dem Ende von main() wieder von vorn anfängt - wie nach einem >> Reset. > > Das würde mich auch mal interessieren, welcher Compiler das sein soll. Derjenige könnte auch ohne sich dessen bewußt zu sein beobachtet haben daß dann nach kurzer Zeit in der Endlosschleife sein Watchdog auslöst. > welcher Compiler das sein soll. Außerdem ist das nicht der Compiler sondern der Startupcode der das Verhalten vor und nach der main bestimmt, meist hat der Chiphersteller sich diesen Code aus dem Finger gesogen und den Compiler hat er sich woanders organisiert und der Compiler ist unschuldig, der Compiler hat ein sehr eng begrenztes Aufgabengebiet, das ganze Drumherum wird separat davon ans verkäufliche Gesamtgebilde drangeflickt aber in seinem innersten ist der Compiler nur ein unschuldiger Compiler.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.