Hallo, in einem meiner Fachbücher "AVR-RISC" (Mittermayr) schlägt der Autor am Ende von main eine Endlosschleife vor; ... while (1); // Endlosschleife "hang-up" return 0; } Soll einen uneindeutigen Programmzustand, nach Abschluss verhindern. Macht das in der Praxis wirklich Sinn?
Welche Alternative schlägst du vor?
__Son´s Bersi__ schrieb: > Macht das in der Praxis wirklich Sinn? Nein. Die Mainloop ist ja schon endlos. Alles dahinter ist unerreichbarer Code. Mit Optimierung wird er auch nicht erzeugt. Peter
__Son´s Bersi__ schrieb: > Hallo, > in einem meiner Fachbücher "AVR-RISC" (Mittermayr) schlägt der Autor am > Ende von main eine Endlosschleife vor; > > ... > while (1); // Endlosschleife "hang-up" > return 0; > } > > Soll einen uneindeutigen Programmzustand, nach Abschluss verhindern. > Macht das in der Praxis wirklich Sinn? Der Grundaufbau JEDES µC Programmes sieht immer so aus
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
Wie sinnvoll es ist, hinter die sowieso obligate Endlosschleife in
main() noch mal eine Endlosschleife als Paranoia-Beruhiger zu setzen -
nun, diese Frage kannst du dir sicherlich selbst beantworten.
> ... nach Abschluss verhindern.
ein µC Programm hat keinen 'Abschluss'. Das Programm läuft vom Anlegen
der Versorgungsspannung an den µC, solange bis du ihm den Saft abdrehst.
Was soll denn passieren, wenn main() jemals verlassen wird? main() wird
nie verlassen! Die Haupschleife IST eine Endlosschleife. Der µC kommt da
nie wieder raus.
__Son´s Bersi__ schrieb: > Soll einen uneindeutigen Programmzustand, nach Abschluss verhindern. > Macht das in der Praxis wirklich Sinn? Naja ^^ Wie definierst du Sinn machen? Es tut natürlich das was du gesagt hast: Es versetzt den µC in eine Endlosschleife, so dass er nichts anderes mehr tut. (Aber aufpassen, dass die leere Schleife nicht irgendwie wegoptimiert wird) Andererseits: Der Compiler sollte das, wenn du in C programmierst selbstständig machen soweit ich informiert bin. Wenn die main() abgeschlossen wurde darf der µC nicht Ammok laufen, deshalb wird auf Assemblerebene wohl sowieso eine Schleife gesetzt (von der du nur nichts mitbekommst), als Absicherung. In Assembler müsstest du dich natürlich selbst darum kümmern. Von dem her ist das ganze wohl eher eine doppelte Absicherung, dass auch wirklich nichts passiert. Ansonsten: Es ist für µC Programe ja gar nicht üblich, dass sie nur ein mal laufen. main() sollte gar nicht auslaufen. Im Normalfall besteht so ein Programm ja immer aus der Struktur
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
Das kommt drauf an was man möchte - einen kontinuierlich arbeitenden µC, meistens bei fertigen Projekten, oder nur einen einzigen Programmdurchlauf wie bei Hallo-Welt-Versuchen. In meinen Programmen ist oft eine hangup-Funktion vorhanden, die im Fehlerfall angesprungen werden kann und die Hardware in einen sicheren Zustand bringt (alles aus und Ruhe). Wenn noch ein Controller-Pin dafür frei war bekommt der auch eine Status-LED, über die man dann einen Fehlercode ausgeben kann.
Ben _ schrieb: > In meinen Programmen ist oft eine hangup-Funktion vorhanden, die im > Fehlerfall angesprungen werden kann Dann steht die aber nicht im Main. Hier ging es um das Main und da ist es Unsinn, toten Code hinein zu schreiben. Peter
Peter Dannegger > Ben _ schrieb >> In meinen Programmen ist oft eine hangup-Funktion vorhanden, die im >> Fehlerfall angesprungen werden kann > > Dann steht die aber nicht im Main. > Hier ging es um das Main und da ist es Unsinn, toten Code hinein zu > schreiben. Vor allen Dingen ist es ja auch logisch sinnlos :-) Wenn der µC es irgendwie schafft, aus der Hauptschleife auszubrechen (Magie? Eingriff eines Erzengel? Ausserirdische? Uri Geller?), dann schafft er es auch, aus der zweiten Emdlosschleife rauszukommen.
Deswegen schreibe ich meine µC-Programme bislang alle in Assembler. Mit dieser dunklen Seite der Magie ist alles möglich. **fg**
der hung-up ist im main()!
int main(void)
{
...
while (1); // Endlosschleife "hang-up"
return 0;
}
Zitat des Autors;
"Um sicher zu gehen, dass sich Ihr Programm nach der Ausführung nicht in
einen unbestimmten Zusatand begibt, empfiehlt es sich auch, diese
Endlosschleife am Ende jedes Programms zu verwenden."
In der Parxis sinnvoll oder überflüssig bis unsinnig?
Brauchst du nicht! Wenn du dein Programm gescheit schreibst, wirst du nie da hin kommen. Und wenn doch, sollte man sich auf Fehlersuche machen und nicht solch unschöne lösungen verwenden. ABER: Eine Endlsschleife in der main() brauchst du, selbst wenn diese selbst leer ist. Sonst wird dein Programm immer wieder von vor anlaufen.
Gast
#3015651
__Son´s Bersi__ schrieb: > In der Parxis sinnvoll oder überflüssig bis unsinnig? Du solltest das so lange verwenden, bis du selbst weisst, wann du es nicht brauchst.
__Son´s Bersi__ schrieb: > In der Parxis sinnvoll oder überflüssig bis unsinnig? Hast du die Antworten eigentlich überhaupt gelesen? __Son´s Bersi__ schrieb: > Zitat des Autors; > "Um sicher zu gehen, dass sich Ihr Programm nach der Ausführung nicht in > einen unbestimmten Zusatand begibt, empfiehlt es sich auch, diese > Endlosschleife am Ende jedes Programms zu verwenden." Wie sieht denn ein typisches Programm von der Stelle des Buches an der du im Moment bist (Ich vermute mal ganz am Anfang) denn aus? Ich könnte mir nur vorstellen, dass er sich momentan noch auf einmalige Vorgänge beschränkt und deshalb die Endlosschleife einbaut. Also irgendwas in die Richtung:
1 | |
2 | |
3 | |
4 | |
Später wird diese dann vermutlich gefüllt und somit implizit zur obligatorischen Endlosschleife, die das Kernstück eines jeden µC-Programm's darstellt. Das sollte dir aber auch klar werden, wenn du 1. die Antworten oben ließt 2. im Buch weiter ließt Wenn der Author wirklich hinter die Hauptschleife noch eine leere setzt ist das eher zweifelhaft ^^
Diese "Diskussion" erinnert mich an die gleiche "Diskussion" über die Genauigkeit von Null Ohm Widerständen. -> /dev/null/
In der Praxis spielt sich der wesentliche Teil des Programms in genau dieser Endlosschleife (oder in Interrupts) ab! Alles vor der Endlosschleife wird nur genau einmal nach dem Einschalten des Mikrocontrollers ausgeführt. Da steht also nur die einmalige Initialisierung, d.h. die Richtung von Ports festlegen, Timer einstellen, Interrupts aktivieren usw. Danach läuft das Programm in die Endlossschleife und arbeitet dort seine Aufgaben ab. Es schaut also z.B. nach, ob Daten per UART empfangen wurden und bearbeitet sie gegebenenfalls. Das macht es einfach so lange, bis man den Strom abschaltet. In manchen Fällen kann man die Endlosschleife auch leer lassen, dann spielt sich das komplette Programm nur in Interrupts ab. Außerdem kann man am Ende der Endlosschleife den Mikrocontroller in den Sleep-Mode versetzen, um Strom zu sparen. Dann macht er einfach nichts, bis er durch ein externes Ereignis wieder aufwacht. Sobald er aufwacht, durchläuft er die Endlosschleife erneut und legt sich nach Abarbeitung aller Aufgaben wieder schlafen. Ein Programm ohne Endlosschleife macht dagegen nicht viel Sinn. Der Mikrocontroller würde nur einmal nach dem Einschalten etwas machen und was danach passiert wäre "undefiniert". Vielleicht erzeugt der Compiler von alleine eine Endlosschleife, vielleicht springt er wieder an den Anfang, vielleicht durchläuft er einfach weiter den Flashsspeicher und führt aus, was kommt ... Selbst wenn das Programm also wirklich nur einmal nach dem Einschalten "Hello World" ausgeben und danach tot sein soll, sollte man dieses "tot sein" explizit in Form einer leeren Endlosschleife hinschreiben. Denn irgendetwas muss der Controller am Ende des Programms immer machen. Nach einer vorhandenen Endlosschleife nochmal eine leere Endlosschleife zu schreiben, ist allerdings sinnlos. Vermutlich wird das in dem Buch aber auch nicht vorgeschlagen, sondern bezieht sich auf ein allererstes Programm, das wirklich nur einmal etwas tut.
Dominik S. schrieb: > (Aber aufpassen, dass die leere Schleife nicht irgendwie wegoptimiert > wird) Kann nicht passieren. Außer der Compiler hat einen Bug, aber dann kann alles mögliche passieren.
Gast
#3015850
Dominik S. schrieb: > Also irgendwas in die Richtung: > int main(void { > setzeLed(); > while(1); > } So hat der Autor des obigen Buches es gemeint. Ansonsten gilt: Beitrag "Re: C-Prog, "hang-up"-Endlosschleife"
Davis schrieb: > Dominik S. schrieb: > >> Also irgendwas in die Richtung: >> int main(void { >> setzeLed(); >> while(1); >> } > > So hat der Autor des obigen Buches es gemeint. Dann sollte er das auch als das bezeichnen, was es ist. So ist das einfach nur 'Schwachsinn', denn ein typisches µC Programm sieht nun mal nicht so aus. Ausser bei den allerersten Programmen, die nichts anderes tun als einmalig eine LED einzuschalten. Ein kurzer Absatz über den typischen Programmaufbau, mit der Erklärung dass es sich hier beim LED-Einschalten um eine Einmalaktion handelt, die deshalb vor die Hauptschleife gezogen wird und jeder kennt sich aus. Aber so ist das mit dem Rumgerede über 'uneindeutige Programmzustände nach Abschluss' einfach nur verwirrend bis missverständlich.
Gast
#3015901
__Son´s Bersi__ schrieb: > "Um sicher zu gehen, dass sich Ihr Programm nach der Ausführung nicht in > einen unbestimmten Zusatand begibt Wenn man schon so paranoid ist, sollte man gleich per Assembler den Programmspeicher bis zum physikalischen Ende mit NOP oder JMP 0 oder JR $ auffüllen. Andrerseits ist das (das "nach der Ausführung") bereits ein unbestimmter Zustand bzw. ein ungewollter. Gruss Reinhard
Im übrigen ist hier auch das int bei main ziemlich überflüssig. Eine Funktion die niemals endet hat sicher keinen sinnvollen return-Wert, also beaucht ein Compiler auch gar nicht versuchen für eine aufrufende Instanz (beim PC das Betriebssystem, und beim Controller???) Platz für einen Rückgabewert im RAM oder Stack zu reservieren. Beim Controller also
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
Ebenso sinnfrei ist int main(Parameterliste)... es gibt beim Controller ja niemand, der das Programm aufruft, also kann auch nie ein Übergabeparameter drin stehen.
Gast
#3015955
Karl Heinz Buchegger schrieb: > Davis schrieb: >> Dominik S. schrieb: >> >>> Also irgendwas in die Richtung: >>> int main(void { >>> setzeLed(); >>> while(1); >>> } >> >> So hat der Autor des obigen Buches es gemeint. > > Dann sollte er das auch als das bezeichnen, was es ist. Wozu noch aufregen?
Bernhard Spitzer schrieb: > Im übrigen ist hier auch das int bei main ziemlich überflüssig. > Eine Funktion die niemals endet hat sicher keinen sinnvollen > return-Wert, also beaucht ein Compiler auch gar nicht versuchen für eine > aufrufende Instanz (beim PC das Betriebssystem, und beim Controller???) > Platz für einen Rückgabewert im RAM oder Stack zu reservieren. dann ist es aber kein C
Vlad Tepesch schrieb: > dann ist es aber kein C Auf was soll sich "es" beziehen? Im zitierten Absatz sind über 10 Substantive. Etwas klarere Kommentare wären dann vielleicht auch wirklich sinnvoll. Oder um es mit Dieter Nuhr zu sagen...
Gast
#3016025
Andreas B. schrieb: > Kann nicht passieren. Außer der Compiler hat einen Bug, aber dann kann > alles mögliche passieren. Was immer wieder unterschätzt wird: Der Programmierer ist eine viel größere Fehlerquelle als der Compiler. Gerade die "aber in der Praxis..."-Rufer sollten das wissen. Selbst bei einem formalen Korrektheitsbeweis (was wohl bis auf in speziellen Situationen eh keiner tut) kann man Fehler machen. So eine Endlosschleife verhindert einen undefinierbaren Zustand, wenn man so dämlich war und doch irgendwie für ein Verlassen der Hauptschleife gesorgt hat. Dann kann man nämlich vernünftig debuggen, weil alle lokalen Variablen noch erreichbar sind. Ich geh sogar noch einen Schritt weiter und pack ans Ende keine nackte Endlosschleife sondern irgendwas, was mir ne LED blinken lässt oder so, damit man auf den ersten Blick sieht, dass man Mist gebaut hat. Nichts ist frustrierender als stundenlang über seltsamem Verhalten zu grübeln, bis man merkt, dass das blöde Ding sich eigentlich nur die ganze Zeit wegen eines Programmfehlers resettet. Reinhard Kern schrieb: > Wenn man schon so paranoid ist, sollte man gleich per Assembler den > Programmspeicher bis zum physikalischen Ende mit NOP oder JMP 0 oder JR > $ auffüllen. Sowas bietet sich für Programme an, die in den Regelbetrieb gehen. Natürlich muss das Programm darauf ausgelegt sein, jederzeit resettet werden zu können, ohne dass irgendwas komisches passiert. Das ist vergleichbar mit BOD/WDT, nur für eine andere Fehlerquelle.
Gast
#3016049
@Sam P. Wie oben schon gesagt wurde. Eine
1 | |
2 | |
3 | |
4 | |
verlässt man auf normalen Wege nicht und wenn doch landet man vermutlich nicht in der hinten angestellten Schleife.
Bernhard Spitzer schrieb: > Auf was soll sich "es" beziehen? Im zitierten Absatz sind über 10 > Substantive. Etwas klarere Kommentare wären dann vielleicht auch > wirklich sinnvoll. Oder um es mit Dieter Nuhr zu sagen... du referierst doch den ganzen Absatz, darüber, dass du den int-Rückgabewert doof findest und ihn weglassen willst. Was werde ich also meinen?
Coder schrieb: > Wie oben schon gesagt wurde. Einewhile(1) > { > // Hier kommt das Hauptprogramm > } > verlässt man auf normalen Wege nicht und wenn doch landet man vermutlich > nicht in der hinten angestellten Schleife. und wenn man irgendwo ein break drin hat, dessen while oder for man wegeditiert hat, weils ebend doch keine schleife brauchte, sondern nur ein if?
Gast
#3016071
@Vlad Tepesch Meine Haupt while-schleife gestalte ich uebersichtlich, in der Form
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
ausserdem ist ein break; kein schönes zur Ablaufsteuerung, ausser beim Switch-Case Konstrukt. Normalerweise gibt der Compiler Warnung bzgl. unerreichbaren Codes; Wenn er aber dieses break doch erreichbar sein sollte müsste die Warnung doch verschwinden?!?
Ich hab nicht gesagt, dass es schön ist, aber einen möglichen Fall aufgezeigt, wo es doch passieren könnte. Bei sauberer Programmierung sollte es nicht passieren.
Gast
#3016087
Jeder hat eine andere Ansicht von sauberer Programmierung. Ich bin eher defensiv und strikt eingestellt und vermeide wenn möglich fehlerträchtige Konstrukte :-)
Sam P. schrieb: > Andreas B. schrieb: >> Kann nicht passieren. Außer der Compiler hat einen Bug, aber dann kann >> alles mögliche passieren. > > Was immer wieder unterschätzt wird: Der Programmierer ist eine viel > größere Fehlerquelle als der Compiler. Gerade die "aber in der > Praxis..."-Rufer sollten das wissen. Selbst bei einem formalen > Korrektheitsbeweis (was wohl bis auf in speziellen Situationen eh keiner > tut) kann man Fehler machen. ?? Darum ging es nicht. Ich habe geschrieben, dass der Compiler eine leere Endlosschleife nicht wegoptimiert, außer sie wird ohnehin nie erreicht. Darauf kann man sich verlassen. Wenn nicht, kann man sich auf gar nichts verlassen.
Gast
#3016560
Ah, dann habe ich deinen Beitrag falsch verstanden/zitiert. Was das gesagte nicht schmälert. Das genannte "break"-Beispiel ist genau so ein Fall. Gerade bei kniffligen Problemen, irgendwelchen Timing-Problemen mit externen Komponenten oder so, wo man schon seit Stunden dran rumwerkelt, weil Theorie und Praxis wieder mal nicht übereinstimmen, passiert es einem, dass man "nur mal eben zum Ausprobieren" etwas einfügt, ändert, teilweise/unvollständig löscht, und BAM... ist die Endlosschleife so endlos gar nicht mehr. Da ist es schon praktisch, einen definierten Fehlerzustand zu haben.
Gast
#3016742
Aus einer bekannten Headerdatei für Pic Programmierung:
#define b asm("nop") //convenient point for breakpoint (debugging)
#define l while(1) //loop for ever (debugging)
auch wenn ich klein l niemals verwenden würde, L könnte ok sein, ein
h oder H für halt wäre besser, i und l wie auch j und k sind bei mir für
loops in for/while und als sehr temporäre Variablen reserviert.
Zudem verwende ich gerne l für strlen wenn dies in derselben Funktion
öfter gebraucht werden.
Für debug wird b umdefiniert, einen Aufruf einer debug Funktion, auf der
ein Breakpoint gesetzt wird, ein Toggeln eines Pins, eine Ausgabe
der aktuellen Proogrammadresse auf der seriellen Schnittstelle oder
SPI, ... ,
Ein Vorschlag:
#define brk() asm("nop") //convenient point for breakpoint (debugging)
#define halt() while(1) brk() //loop for ever (debugging)
und beim Debug brk undefinieren und diese Funktion schreiben,
aber man könnte auch b und h verwenden, den Bezeichner l finde ich zu
gefährlich.
Gast
#3016767
Karl Heinz Buchegger schrieb: > Wenn der µC es irgendwie schafft, aus der Hauptschleife auszubrechen > (Magie? Eingriff eines Erzengel? Ausserirdische? Uri Geller?), dann > schafft er es auch, aus der zweiten Emdlosschleife rauszukommen. Da reicht ein *break;* und schon isser raus. SW-Fehler gibt es doch immer. ;-) Und dann isses gut wenn man das abfängt. Sam P. schrieb: > So eine Endlosschleife verhindert einen undefinierbaren Zustand, wenn > man so dämlich war und doch irgendwie für ein Verlassen der > Hauptschleife gesorgt hat. Dann kann man nämlich vernünftig debuggen, > weil alle lokalen Variablen noch erreichbar sind. Ein weiter Vorteil ist, das die Peripherie nicht irgendwelche unkontrollierten Dinge tut. Karl Heinz Buchegger schrieb: > Der Grundaufbau JEDES µC Programmes sieht immer so aus > int main() > { > falls Interrupts benutzt werden: sei(); > while( 1 ) { > > } > } Nicht immer. Schau die Beispiele zum MSP430 an. Anstatt endless loop wird der µC in einen Low Power Mode versetzt. Den behält nach der Bearbeitung einer ISR bei. Es wird kein Befehl mehr im Hauptprogramm ausgeführt. ;-)))
16 Bit schrieb: > Karl Heinz Buchegger schrieb: >> Wenn der µC es irgendwie schafft, aus der Hauptschleife auszubrechen >> (Magie? Eingriff eines Erzengel? Ausserirdische? Uri Geller?), dann >> schafft er es auch, aus der zweiten Emdlosschleife rauszukommen. > Da reicht ein *break;* und schon isser raus. SW-Fehler gibt es doch > immer. ;-) > > Und dann isses gut wenn man das abfängt. Wenn du dich wirklich irrtümlich aus der Hauptschleife rausbreakst, dann kannst du dich auch der Default-Endlossschleife im Runtime System anvertrauen, in die ein AVR beim gcc einläuft, wenn er jemals aus main() zurückkommt. Endlosschleife mit abgeschalteten Interrupts - diese Fehlersituation ist garantiert nicht zu übersehen.
DANKE für die zahlreichen Rü´s! Die Letzten schweiften mir allerdings zu weit von meinem Thema ab. Die Kontroversen zeigen mir, dass es sich eher um eine Grundeinstellung handelt. Zusätzliche "hang-up" für zusätzliche Sicherheit, wenn die main() nur ein mal durchlaufen wird.
__Son´s Bersi__ schrieb: > Die Kontroversen zeigen mir, dass es sich eher um eine Grundeinstellung > handelt. Zusätzliche "hang-up" für zusätzliche Sicherheit, wenn die > main() nur ein mal durchlaufen wird. Die Sicherung für die Sicherung. Irgendwann ist es genug. Wenn man aus der Hauptschleife rauskommt, dann ist das ein definitiv ein Fehler. Der gehört korrigiert und nicht abgesichert.
Gast
#3020667
Mein Senf dazu: Konstrukte, die helfen, die eigenen Pogrammierfehler zu verdecken, tragen kaum zur Sicherheit bei...
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.