mikrocontroller.net

Forum: Compiler & IDEs Technik für zyklisches erhöhen eines Wertes: if versus modulo


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Markus M. (adrock)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

was ist eigentich "best practice" für das zyklische erhöhen eines Wertes 
X von 0...(Y-1):
if (++X >= Y) X = 0;

oder
X = (X + 1) % Y;

Oder ist das abhängig davon, ob die CPU Division/Modulo in HW 
beherrscht?

Version 2 sieht natürlich erstmal eleganter aus, dürfte aber auf fast 
allen CPUs langsamer sein (sofern Y nicht 2^irgendwas ist).

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
2 lesenswert
nicht lesenswert
Bei mir wäre es:
++x;
if (x == y)
    x = 0;

Man kriegt kein Geld zurück für's Zeilen sparen.

Autor: Stefan S. (Gast)
Datum:

Bewertung
-4 lesenswert
nicht lesenswert
@ Rolf M.

Na du wirst wohl Zeilenweise bezahlt, dann mach doch gleich
++x;
if (x == y)
  {
    x = 0;
  }

Haste wieder ein paar Cent verdient. Aber im erst, deine Antwort war 
nicht sehr hilfreich. Hatte ich von dir nicht vor ein paar Jahren mal 
was besseres gelesen?

Und zum Thema: Div bzw % wird man stets zu vermeiden versuchen, Ausnahme 
wäre, wenn durch eine konstante Zweierpotenz dividiert wird, da der 
Compiler dann nicht dividieren muss, sondern ein Shift verwenden kann. 
Wobei man aber auch da aufpassen muss, ein reines Shift bekommt man nur 
für Unsigned, für Signed generiert der Compiler in der Regel noch einen 
Vorzeichentest.

Du kannst dir den Code leicht ansehen auf  https://godbolt.org/

Autor: Mach (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Markus M. schrieb:
> was ist eigentich "best practice" für das zyklische erhöhen eines Wertes
X = X + 1;
if (X >= Y) X = 0;

Autor: A. S. (achs)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Stefan S. schrieb:
> Aber im erst, deine Antwort war nicht sehr hilfreich.

Doch. Es war der kürzeste saubere weg. Alles nachfolgende ist zusammen 
weniger wert. Wenn dann die Rede von sequence-points kommt, schaltet der 
Up ab und macht es am Ende deswegen weiterhin falsch.

: Bearbeitet durch User
Autor: Stefan S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan S. schrieb:
> ein Shift verwenden kann

Na gut, für ein Modulo durch Zweierpotenz natürlich kein Shift, sondern 
eine Maskierung durch AND.

Übrigens hatte ich schon des öfteren gelesen, dass ein Test auf 
grössergleich langsamer sein kann als ein einfacher Test auf Gleichheit. 
Wenn es ein Test auf Gleichheit tut, verwende ich daher jenen.

Autor: Peter p (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus M. schrieb:
> Version 2 sieht natürlich erstmal eleganter aus, dürfte aber auf fast
> allen CPUs langsamer sein (sofern Y nicht 2^irgendwas ist).

Du hast dir die Frage selbst beantwortet.

Autor: Peter p (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan S. schrieb:
> Stefan S. schrieb:
> ein Shift verwenden kann
>
> Na gut, für ein Modulo durch Zweierpotenz natürlich kein Shift, sondern
> eine Maskierung durch AND.
>
> Übrigens hatte ich schon des öfteren gelesen, dass ein Test auf
> grössergleich langsamer sein kann als ein einfacher Test auf Gleichheit.
> Wenn es ein Test auf Gleichheit tut, verwende ich daher jenen.

Was ist dann wenn der Wert aus iwelchen Gründen mal größer ist? >= und 
== sind beide zu vermeiden. Besser ist die Prüfung auf > y-1

Beitrag #5678541 wurde vom Autor gelöscht.
Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan S. schrieb:
> @ Rolf M.
>
> Na du wirst wohl Zeilenweise bezahlt, dann mach doch gleich
> ++x;
> if (x == y)
>   {
>     x = 0;
>   }
>
> Haste wieder ein paar Cent verdient.

Was ich damit sagen wollte: Ich finde es schlecht lesbar, wenn das alles 
in eine Zeile gequetscht ist. Man darf auch mehrere Zeilen verwenden, 
wenn das der Lesbarkeit dienlich ist.

> Aber im erst, deine Antwort war nicht sehr hilfreich.

Das ist schade. Aber deine Frage war, was "best practice" sei. Für mich 
ist es das, was ich in meiner Antwort geschrieben habe. Wenn dir das 
nicht hilft, kann ich ja nichts dafür.

> Und zum Thema: Div bzw % wird man stets zu vermeiden versuchen, Ausnahme
> wäre, wenn durch eine konstante Zweierpotenz dividiert wird, da der
> Compiler dann nicht dividieren muss, sondern ein Shift verwenden kann.

Das hängt natürlich auch vom Prozessor ab. Aber ich finde es auch von 
der Lesbarkeit her weniger gut.

Peter p schrieb:
> Was ist dann wenn der Wert aus iwelchen Gründen mal größer ist? >= und
> == sind beide zu vermeiden. Besser ist die Prüfung auf > y-1

Welche Gründe sollten das sein außer einem Fehler im Programm oder einem 
Fehler in der Hardware? Für ersteres würde ich dann lieber ein assert 
oder sowas verwenden, damit man den Fehler auch sieht.

: Bearbeitet durch User
Autor: Peter p (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Übertragungsfehler, Speicherfehler, Programmierfehler, umgekipptes Bit 
durch Gammastrahlung...

Autor: Peter p (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klar wäre es sinnvoll sicherzustellen dass solche Fehler nicht 
auftreten. Aber es ist schlicht die schnellste und sicherste Variante, 
also warum was anderes machen...

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin kein Freund davon, Fehler zu verstecken. Denn das fällt einem 
dann irgendwann später auf den Fuß und erschwert das Debugging. Also ja, 
ein >= ist von mir aus ok, aber dann sollte der Fall > auch explizit als 
Fehler behandelt werden, denn der wird ja nur erreicht, wenn irgendwas 
schief gelaufen ist.

: Bearbeitet durch User
Autor: Stefan S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ach so, ich hatte vergessen neben  https://godbolt.org/ auf die Tabelle 
mit den Instruktionen zu verweisen:

https://www.agner.org/optimize/instruction_tables.pdf

Auch bei aktuellen Prozessoren wie Skylake sind die DIV Operation eher 
langsam bzw. haben eine hohe Latenz.

Autor: Markus M. (adrock)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Danke, damit wäre meine Annahme bestätigt.

Zur Diskussion um die Syntax: Ich schreibe es normalerweise auch nicht 
immer in eine Zeile, habe es nur in eine Zeile gequetscht um die 
(syntaktische) Länge als Entscheidungskriterium auszublenden, das 
Gegenteil habe ich erreicht :-)

Allgemein versuche ich Zuweisungsoperationen (also auch Increment / 
Decrement) aus den IF-Statements rauszuhalten, aber das ist nunmal 
Geschmackssache.

PS:
Die Seite https://godbolt.org/ "Comiler Explorer" ist cool! Da z.B. der 
Cortex-M4 bei der Division keinen Rest zurückgibt, wird modulo offenbar 
über Multiplikation realisiert :) Die Variante mit dem "if" ist dagegen 
super kurz (modulo 10 im Beispiel):

add     r0, r0, #1
cmp     r0, #10
moveq   r0, #0

Autor: Peter p (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Ich bin kein Freund davon, Fehler zu verstecken. Denn das fällt
> einem dann irgendwann später auf den Fuß und erschwert das Debugging.
> Also ja, ein >= ist von mir aus ok, aber dann sollte der Fall > auch
> explizit als Fehler behandelt werden, denn der wird ja nur erreicht,
> wenn irgendwas schief gelaufen ist.

Manche nennen es Fehler verstecken, andere neben es robuste 
Programmierung. Einer der letzteren war damals mein Prof für Embedded 
Systems. Daran orientiert ich mich erstmal.

Autor: Bernd (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
warum wurde vorgeschlagen
if (x > y-1) ...
statt
if (x >= y) ...

Autor: Peter p (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Weil man y-1 einmal vor der Schleife bestimmen kann und > i. A. 
schneller ist als >=. Könnte aber auch veraltet sein.

Autor: A. S. (achs)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter p schrieb:
> Weil man y-1 einmal vor der Schleife bestimmen kann und > i. A.
> schneller ist als >=. Könnte aber auch veraltet sein.

Das hat dann aber mit Hochsprachenprogrammierung nichts mehr zu tun. 
Solch Optimierungen kann der Compiler.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter p schrieb:
> Rolf M. schrieb:
>> Ich bin kein Freund davon, Fehler zu verstecken. Denn das fällt
>> einem dann irgendwann später auf den Fuß und erschwert das Debugging.
>> Also ja, ein >= ist von mir aus ok, aber dann sollte der Fall > auch
>> explizit als Fehler behandelt werden, denn der wird ja nur erreicht,
>> wenn irgendwas schief gelaufen ist.
>
> Manche nennen es Fehler verstecken, andere neben es robuste
> Programmierung. Einer der letzteren war damals mein Prof für Embedded
> Systems. Daran orientiert ich mich erstmal.

Naja, wie gesagt: Ein Fehler ist passiert. Wenn der Wert falsch ist, hat 
das Programm schon Mist gebaut. Man hat bei >= immerhin den Vorteil, 
dass sich die Auswirkungen des Fehlers nicht weiter fortsetzen. Ein 
Fehler ist aber trotzdem passiert, dessen muss man sich eben bewusst 
sein.

Peter p schrieb:
> Weil man y-1 einmal vor der Schleife bestimmen kann und > i. A.
> schneller ist als >=. Könnte aber auch veraltet sein.

Mir wäre keine Architektur bekannt, wo das so ist.

Autor: Peter p (Gast)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
Du diskutierst gerne was?

Autor: Stefan S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter p schrieb:
> Du diskutierst gerne was?

Manchmal schon, aber nicht mit Dir!

Nach einigem Nachdenken könnte ich mir vorstellen, dass auf einigen 
Architekturen x == 0 schneller ist als x >= 0 bzw x <= 0. Grund: Wenn x 
in ein Register geladen wird, wird womöglich schon das ZERO Flag 
gesetzt, so dass ein nachfolgendes CMP bei x == 0 entfallen kann. Leider 
nur eine Vermutung, bei x86 ist das wohl nicht so, da Mov keinerlei 
Flags setzt.

Autor: Peter p (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum tust du es dann? Hast du mitbekommen dass dort steht es sei 
möglicherweise veraltet?

Autor: Stefan S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Ich bin kein Freund davon, Fehler zu verstecken.

Ich eher auch nicht.

Und nicht nur, dass man durch ein >= wo ein == stehen sollte einen 
womöglichen Logikfehler versteckt: Man macht es dem Compiler und einem 
Leser des Codes schwieriger die tatsächliche Logik zu verstehen. Der 
Compiler kann dann womöglich schlechter optimieren oder Fehler erkennen.

Das mit dem Bitkippen durch radioaktive Strahlung -- da wird ein >= den 
Marsrover auch nicht retten.

Autor: Peter S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan S. schrieb:
> Und nicht nur, dass man durch ein >= wo ein == stehen sollte einen
> womöglichen Logikfehler versteckt: Man macht es dem Compiler und einem
> Leser des Codes schwieriger die tatsächliche Logik zu verstehen.

Genauso ist es. Hätte ich nicht besser ausdrücken können.

Und wenn man den Wert tatsächlich überprüfen will/muss, dann macht man 
das dort, wo der Wert verwendet wird (z.B. mit einem Assert).

Autor: Peter D. (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich hab mir dafür ein Macro geschrieben, da man es häufig braucht.
Der AVR-GCC macht daraus auch kürzeren Code, als mit if.
#define ROLLOVER( x, max )  x = ++x >= max ? 0 : x
          // count up and wrap around

Autor: Peter D. (peda)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Man hat bei >= immerhin den Vorteil,
> dass sich die Auswirkungen des Fehlers nicht weiter fortsetzen.

Genau das ist ein sehr wichtiger Punkt. Ich sichere damit ab, daß nur 
die Funktion selber Fehler macht.
Bei einem == dagegen, kann ich mir einen völlig anderen Speicherbereich 
zerstören und lege mir dann richtig die Karten.
Der Test auf >= ist defensive Programmierung und daher eindeutig 
vorzuziehen.

Ich hab schonmal ewig und 3 Tage Fehler in der falschen Task gesucht, 
daher vermeide ich wildgewordene Pointer, wo immer es geht.

: Bearbeitet durch User
Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Rolf M. schrieb:
>> Man hat bei >= immerhin den Vorteil,
>> dass sich die Auswirkungen des Fehlers nicht weiter fortsetzen.
>
> Genau das ist ein sehr wichtiger Punkt.

Naja, geht so.

> Ich sichere damit ab, daß nur die Funktion selber Fehler macht.


> Bei einem == dagegen, kann ich mir einen völlig anderen Speicherbereich
> zerstören und lege mir dann richtig die Karten.

Wenn der >-Teil anspricht, war der Wert bereits zu groß, und die ganzen 
potenziellen Probleme, die daraus entstehen können, sind bereits da. Das 
Kind ist schon in den Brunnen gefallen. Mit >= statt == sorge ich 
lediglich dafür, dass nicht noch ein zweites Kind reinfällt.

Autor: A. S. (achs)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Ich hab mir dafür ein Macro geschrieben, da man es häufig braucht.
> Der AVR-GCC macht daraus auch kürzeren Code, als mit if.
>
> #define ROLLOVER( x, max )  x = ++x >= max ? 0 : x
>           // count up and wrap around
> 

Und das gibt keine Sequnce-Point-Undefined-Behaviour?

(Ich hoffe, dass ist nur so hingeschrieben und nicht der originale Code, 
oder?)

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
A. S. schrieb:
> Und das gibt keine Sequnce-Point-Undefined-Behaviour?

Nö. Der ternäre Operator hat nach der Bedingung einen Sequenzpunkt.

Peter D. schrieb:
> Ich hab schonmal ewig und 3 Tage Fehler in der falschen Task gesucht,
> daher vermeide ich wildgewordene Pointer, wo immer es geht.

Daher ja die Assertion oder andere Meldung eines Fehlers, wenn x mal > 
max sein sollte.

: Bearbeitet durch User
Autor: Peter S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Ich hab mir dafür ein Macro geschrieben, da man es häufig braucht.
> Der AVR-GCC macht daraus auch kürzeren Code, als mit if.#define
> ROLLOVER( x, max )  x = ++x >= max ? 0 : x
>           // count up and wrap around

Dieses Macro macht betroffen...

Mal von den fehlenden Klammern abgesehen, erfolgt Zugriff und 
Modifikation einer Variablen in einem Ausdruck.

Autor: Peter D. (peda)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Peter S. schrieb:
> Mal von den fehlenden Klammern abgesehen

Ich setze grundsätzlich keine überflüssigen Klammern.

Autor: Peter S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Ich setze grundsätzlich keine überflüssigen Klammern.

Wenn du meinst :D

Autor: A. S. (achs)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Nö. Der ternäre Operator hat nach der Bedingung einen Sequenzpunkt.

Ja, das habe ich völlig übersehen. Und im Gegensatz zu sonstigen 
#defines finde ich hier tatsächlich kaum relvante Fälle, wo die fehlende 
Klammerung ein (unerkanntes) Problem darstellt.

Autor: S. R. (svenska)
Datum:

Bewertung
3 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Was ich damit sagen wollte: Ich finde es schlecht lesbar, wenn das alles
> in eine Zeile gequetscht ist. Man darf auch mehrere Zeilen verwenden,
> wenn das der Lesbarkeit dienlich ist.

Treibt man das weiter, bekommt man zwar sehr viele, dafür aber 
aussagearme Zeilen. Also ähnlich wie bei Assembler. :-)

Peter p schrieb:
> Weil man y-1 einmal vor der Schleife bestimmen kann und > i. A.
> schneller ist als >=. Könnte aber auch veraltet sein.

Das ist Aufgabe des Compilers, der sollte das auf den betroffenen 
Architekturen können. Ob die Vergleiche unterschiedlich schnell sind, 
hängt von der Architektur ab.

Stefan S. schrieb:
> Und nicht nur, dass man durch ein >= wo ein == stehen sollte einen
> womöglichen Logikfehler versteckt: Man macht es dem Compiler und einem
> Leser des Codes schwieriger die tatsächliche Logik zu verstehen.

Ich weiß ja nicht.

Nehmen wir mal einen Software-Timer. Der zählt immer brav von 0 bis N 
hoch und soll nicht überlaufen. Soweit, sogut. Euer Argument 
funktioniert, solange ich den Vergleichswert N niemals bei aktivem Timer 
ändere.

Andernfalls habe ich jetzt einen subtilen Bug in meinem Programm, der 
möglicherweise sehr lange unentdeckt bleiben und viel Schaden anrichten 
kann, dazu noch schwer zu debuggen ist.

Um den Bug zu vermeiden, brauche ich jetzt zusätzliche Logik in der 
Änderungsfunktion, was möglicherweise neue Bugs erschafft. Hätte ich 
defensiv programmiert, wäre das nicht nötig.

Nun ist mein System überlastet und kann nicht schnell genug auf den 
Timer reagieren und verpasst einen Timerwert. Plötzlich wartet mein 
System nicht einen Tick zu lange, sondern im Zweifelsfall UINT32_MAX, 
bevor es wieder reagiert. Das kommt einem Absturz sehr nahe. Hätte ich 
defensiv programmiert, wäre das nicht passiert.

Irgendeine schlaue Nase kam nun auf die Idee, den Zählerwert auf float 
umzustellen, weil die neue Version der Hardware dafür geeignet ist und 
die Anwendung davon profitiert. Plötzlich triggert der Timer garnicht, 
weil man Float-Werte nicht sinnvoll auf Gleichheit testen kann. Hätte 
ich defensiv programmiert, wäre das kein Problem.

Und weil mein System vorerst nur ein kleiner Controller ist, ist eine 
sichtbare Fehlermeldung nicht möglich bzw. würde den Nutzer nur 
verwirren. Ob das System nun direkt abstürzt (weil ein abort() triggert) 
oder ob es sich aufhängt (weil der Timer nicht mehr timert), 
interessiert den Kunden nicht weiter - sauer ist er in beiden Fällen.

Mir ist Code lieber, der keine Annahmen über seinen Einsatz treffen 
muss, weil er defensiv formuliert wurde und sich damit immer 
deterministisch verhält. Dazu gehört auch, dass Funktionen anstatt 
ordentlicher Fehlererkennung lieber so geschrieben sein sollten, dass 
sie grundsätzlich nicht fehlschlagen können. Sowas geht dann auch in die 
Richtung API-Design.

Autor: 555nase (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Gut erklärt! So sehe ich das auch. Ehrlich gesagt verstehe ich auch 
nicht, wieso ein >= die Logik schwerer zu verstehen macht. Ich würde 
umgekehrt bei einem == immer stutzig werden (aber ich bin auch auf 
defensive Programmierung gepolt). Spätestens wenn der Timer das erste 
Mal irgendein kleines Problem hätte, würde ich da sofort testweise ein 
>= draus machen und das dann auch so lassen, selbst wenn das Problem 
woanders lag.
Hat jemand noch mehr sinnvolle Beispiele für defensive Programmierung? 
Da sind vielleicht auch für Anfänger hilfreiche Sachen dabei.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
S. R. schrieb:
> Rolf M. schrieb:
>> Was ich damit sagen wollte: Ich finde es schlecht lesbar, wenn das alles
>> in eine Zeile gequetscht ist. Man darf auch mehrere Zeilen verwenden,
>> wenn das der Lesbarkeit dienlich ist.
>
> Treibt man das weiter, bekommt man zwar sehr viele, dafür aber
> aussagearme Zeilen. Also ähnlich wie bei Assembler. :-)

Klar - man kann alles ad absurdum führen. Ich sage ja nicht, dass man so 
viele Zeilen wie möglich nutzen soll, sondern so viele wie sinnvoll. Und 
oben finde ich es sinnvoll, nicht alles in eine Zeile zu schreiben.

> Nehmen wir mal einen Software-Timer. Der zählt immer brav von 0 bis N
> hoch und soll nicht überlaufen. Soweit, sogut. Euer Argument
> funktioniert, solange ich den Vergleichswert N niemals bei aktivem Timer
> ändere.
>
> Andernfalls habe ich jetzt einen subtilen Bug in meinem Programm, der
> möglicherweise sehr lange unentdeckt bleiben und viel Schaden anrichten
> kann, dazu noch schwer zu debuggen ist.
>
> Um den Bug zu vermeiden, brauche ich jetzt zusätzliche Logik in der
> Änderungsfunktion, was möglicherweise neue Bugs erschafft. Hätte ich
> defensiv programmiert, wäre das nicht nötig.

Dann kann es aber einen Zyklus geben, in dem der Zähler weder bis zum 
alten N, noch bis zum neuen N zählt, sondern so weit, wie er eben gerade 
war, als N geändert wurde. Das kann auch ein sehr subtiler Bug sein.

> Und weil mein System vorerst nur ein kleiner Controller ist, ist eine
> sichtbare Fehlermeldung nicht möglich bzw. würde den Nutzer nur
> verwirren.

Ob ihn das nun mehr verwirrt als ein Fehlverhalten ohne Meldung?
Aber es war auch nicht so sehr als Meldung für den Benutzer gedacht, 
sondern für den Entwickler. Der soll ja wenigstens merken, dass hier ein 
Zustand eingetreten ist, den es eigentlich gar nicht geben dürfte.
Ein Benutzer kann mit einem "Fehler: MyCycleCounter ist 42. Das hätte 
nicht passieren dürfen!" oder einer LED, die einem das per Morsecode 
mitteilt, ja nun auch nicht wirklich was anfangen.

> Dazu gehört auch, dass Funktionen anstatt ordentlicher Fehlererkennung
> lieber so geschrieben sein sollten, dass sie grundsätzlich nicht
> fehlschlagen können.

Also so, dass es keinen falschen Zählerwert geben kann und somit ein 
Vergleich auf >= gar nicht nötig wäre?

Autor: W.S. (Gast)
Datum:

Bewertung
-3 lesenswert
nicht lesenswert
555nase schrieb:
> Hat jemand noch mehr sinnvolle Beispiele für defensive Programmierung?

Ja, natürlich.
Die erste Regel lautet, daß man sich nie drauf verlassen sollte, daß "am 
Anfang ja sowieso alles genullt" sei.

Ich hatte da mal ein böses Fehlverhalten des FatFS von Chan nach Wechsel 
der SD-Karte. Das kam daher, daß Chan sich voll auf "ist am Anfang 
genullt" verlassen hat. War vor Jahren, kann sein, daß er das inzwischen 
entbugt hat.

W.S.

Autor: Carl D. (jcw2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> 555nase schrieb:
>> Hat jemand noch mehr sinnvolle Beispiele für defensive Programmierung?
>
> Ja, natürlich.
> Die erste Regel lautet, daß man sich nie drauf verlassen sollte, daß "am
> Anfang ja sowieso alles genullt" sei.
>
> Ich hatte da mal ein böses Fehlverhalten des FatFS von Chan nach Wechsel
> der SD-Karte. Das kam daher, daß Chan sich voll auf "ist am Anfang
> genullt" verlassen hat. War vor Jahren, kann sein, daß er das inzwischen
> entbugt hat.
>
> W.S.

Falls das so ist, dann sollte man die C-Runtime entbuggen und nicht 
darauf hoffen, daß weimal nullen mehr Bits löscht. So ein 
memset(__bss_start,0,__bss_end-__bss_start)
ist ja nicht so kompliziert.
Man könnte das aber ja auch das 2. Mal auch nicht hinbekommen. Was dann, 
3x(/4x/5x) nullen?

Wenn man sich wirklich auf keinen Sprachstandard verlassen will, dann 
sollte man Compiler meiden und seine Programme Bit-weise 
zusammenklöppeln. Wenn man das sogar wörtlich nimmt erhält man das 
einzig optisch verifizierbare ROM. Hat ja sogar für die Mondladung 
gereicht. ;-)

Autor: Rufus Τ. F. (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
4 lesenswert
nicht lesenswert
W.S. schrieb:
> Die erste Regel lautet, daß man sich nie drauf verlassen sollte, daß "am
> Anfang ja sowieso alles genullt" sei.

Das hast Du schon mal erzählt, aber das bedeutet, daß der Compiler bzw. 
die Laufzeitumgebung/der Starupcode effektiv kaputt ist. Und dann kann 
man sich sowieso auf überhaupt gar nichts verlassen.

Autor: Yalu X. (yalu) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Die erste Regel lautet, daß man sich nie drauf verlassen sollte, daß "am
> Anfang ja sowieso alles genullt" sei.


Ja, man sollte generell immer mit dem Schlimmsten rechnen. Deswegen
schreibt man statt

  c = a + b;

getreu dem Motto "doppelt genäht hält besser" folgendes:

  c1 = a + b;
  c2 = a + b;
  c3 = a + b;

  if(c1 == c2 || c1 == c3)
    c = c1;
  else if(c2 == c3)
    c = c2;
  else {
    fprintf(stderr, "Schwerer Fehler\n");
    exit(1);
  }

  // c enthält jetzt mit recht hoher Wahrscheinlichkeit das richtige
  // Ergebnis

Damit werden sporadisch auftretende Additionsfehler elegant unter den
Teppich gekehrt ;-)

Anmerkung: Optimierungen durch den Compiler müssen deaktiviert werden.

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> mit recht hoher Wahrscheinlichkeit

Da gibt's übrigens eine eigene Programmiersprache, die sich dieser 
Thematik widmet. In Java2k werden Instruktionen nur mit einer gewissen 
Wahrscheinlichkeit ausgeführt. Da kann man dann damit experimentieren, 
wie man es mit möglichst wenig Rechenaufwand schafft, die 
Wahrscheinlichkeit zu maximieren.
Und zusätzlich werden übrigens statt unleserlichen Bezeichnern viel 
besser lesbare Magic Numbers verwendet:

https://de.wikipedia.org/wiki/Liste_von_Hallo-Welt-Programmen/Sonstige#Java2K

: Bearbeitet durch User
Autor: W.S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Carl D. schrieb:
> Falls das so ist, dann sollte man die C-Runtime entbuggen und nicht
> darauf hoffen, daß weimal nullen mehr Bits löscht. So ein
> memset(__bss_start,0,__bss_end-__bss_start)
> ist ja nicht so kompliziert.
> Man könnte das aber ja auch das 2. Mal auch nicht hinbekommen. Was dann,
> 3x(/4x/5x) nullen?

Du hast den Knackpunkt nicht verstanden: Es ist völlig NORMAL, daß man 
bei laufendem Controller ne SD-Karte herausnehmen und ne andere 
reinstecken kann, ohne daß der ganze Controller durch's Reset gehen muß.

Stell dir doch mal vor, du müßtest deinen PC jedesmal runterfahren, wenn 
du nen USB-Stick raus und einen anderen reinstecken willst.

W.S.

Autor: Frank M. (ukw) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
W.S. schrieb:
> Du hast den Knackpunkt nicht verstanden: Es ist völlig NORMAL, daß man
> bei laufendem Controller ne SD-Karte herausnehmen und ne andere
> reinstecken kann, ohne daß der ganze Controller durch's Reset gehen muß.

Eben. Also ist der Controller nicht in den Reset gegangen und es gab 
auch keine Notwendigkeit, Static Data wieder zu nullen. Du schilderst 
ein Phantom.

: Bearbeitet durch Moderator

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.