[if - else if - else] oder [switch - case - default] ?
-
Lichtlein schrieb:
Also, benutzt switch/case wann immer möglich. Ist leichter zu warten.
April, April!
-
@Lichtlein:
Vorweg: ja, du hast Recht, dein Beispiel "funktioniert" (in dem Sinn dass sich da mit if-else-if ein Problem ergibt welches sich mit switch() nicht ergibt. I stand corrected.----
Ich hab schon einigen Treiber-Code gesehen, aber noch keinen derart schrottigen.
Es ist ja nicht so das ich mir das aus den Finger gesogen habe. Häufig entsteht solcher Code über lange Zeit weil der Chip sich weiter Entwickelt und neue Funktionen bekommt. Und dann lass jemanden am Treiber Programmieren der aus der Anwendungs-Entwicklung kommt. Da kommen Sachen raus.

Man kann die Zugriffe auch in ReadXxxRegister() und WriteYyyRegister() Funktionen packen. Dann versteht auch Mr. Anwendungsprogrammiererwastler was da abgeht. Mal ganz davon abgesehen dass Mr. Anwendungsprogrammiererwastler in der Treiberentwicklung nix verloren hat. Zumindest nicht ohne Einschulung.
Was als Aussage für mich übrig bleibt: schlechte Programmierer schreiben schlechte Programme. Das lässt sich aber mMn. nicht dadurch fixen/verbessern dass man ihnen verbietet if-else-if zu verwenden (bzw. empfiehlt switch stattdessen zu verwenden). Auch wenn manche anderen Programmierer glauben dass es ne super Taktik ist um Probleme zu vermeiden.
-
Lichtlein schrieb:
Also, benutzt switch/case wann immer möglich. Ist leichter zu warten.
Habe heute in einem Treiber diesen Code gesehen und an diesen Thread hier gedacht:
switch (value) { default: ... blabla(value); ... break; }... ja, ein switch ohne case
ist sogar noch einfacher zu warten 
-
Ah, ein Treiberentwickler der Zukünftige Entwicklungen berücksichtigt !! Sehr Löblich.

Lichtlein
-
Genau, toller Code!
Es gibt da noch mehr Sachen, z.B. switch mit nichts:switch (value) { default: ; }oder ein switch-case ohne default-Zweig:
switch (value) { case A: ... break; case B: ... break; }oder ein switch-case mit einem mekrwürdigen break:
switch (value) { case A: ... ret = blabla(); ... if (ret) break; case B: case C: break; }in case B und C steht kein Code, einfach break. Toll...
-
abc.w schrieb:
oder ein switch-case ohne default-Zweig:
Wenn bereits vorhin alle möglichen Fälle (in denen etwas passiert) abgedeckt sind, ist
defaultunnötig. Du schreibst ja auch nicht zu jedemifeinelse.abc.w schrieb:
in case B und C steht kein Code, einfach break. Toll...
Wo liegt das Problem? So macht man wenigstens explizit, dass es noch die Fälle
BundCgibt und dass in diesen Fällen nichts getan wird. Noch ausdrücklicher ginge es nur noch mit einem Kommentar.
-
Nexus schrieb:
abc.w schrieb:
oder ein switch-case ohne default-Zweig:
Wenn bereits vorhin alle möglichen Fälle (in denen etwas passiert) abgedeckt sind, ist
defaultunnötig. Du schreibst ja auch nicht zu jedemifeinelse.Alle möglichen Fälle abdecken - so was gibt es nicht. Ich schreibe gerne immer ein
default, auch wenn es "nicht notwendig" ist, und mache noch z.B. so was:switch (value) { case A: ... break; case B: ... break; default: atomic_inc(&unexpected_case_cnt); break; }... und manchmal schreibe ich auch ein
elsezu jedemif, manchmal mit einem Kommentar drin, z.B.:if (something) { a = blabla(); } else { /* Do not touch a in this case here, because bla bla bla bla */ a = a; }Nexus schrieb:
abc.w schrieb:
in case B und C steht kein Code, einfach break. Toll...
Wo liegt das Problem? So macht man wenigstens explizit, dass es noch die Fälle
BundCgibt und dass in diesen Fällen nichts getan wird. Noch ausdrücklicher ginge es nur noch mit einem Kommentar.Das Problem ist, dass für
ret == 0wird auch B und C ausgeführt. In diesem Fall steht da einfach einbreakund es passiert nichts, bis jemand dort seinen Code einfügt...
-
abc.w schrieb:
Nexus schrieb:
abc.w schrieb:
oder ein switch-case ohne default-Zweig:
Wenn bereits vorhin alle möglichen Fälle (in denen etwas passiert) abgedeckt sind, ist
defaultunnötig. Du schreibst ja auch nicht zu jedemifeinelse.Alle möglichen Fälle abdecken - so was gibt es nicht.
Warum nicht? Vielleicht habe ich vorher bereits sichergestellt, dass die Variable einen von mir erwarteten Wert hat. Vielleicht will ich nur eine zusätzliche Operation für ein paar Spezialfälle machen und für alles andere soll gar nichts getan werden.
... und manchmal schreibe ich auch ein
elsezu jedemif, manchmal mit einem Kommentar drin, z.B.:if (something) { a = blabla(); } else { /* Do not touch a in this case here, because bla bla bla bla */ a = a; }Finde ich nicht schön, vor allem die Selbstzuweisung. Ich würd das "else" wahrscheinlich mit in den Kommentar tun.
-
Mal unabhängig von dem, was der Compiler macht:
Wenn es wirklich nur 2 Werte sind und sonst default-Fälle, und es einigermaßen vorhersehbar ist, dass die 2 Fälle nicht später mal um einige mehr erweitert werden, würde ich zu einem
if .. else if .. elsetendieren.Bei
defaultimswitch-Block gehe ich nach der Faustregel, dass die behandelten Fälle (meistens, wie schon gesagt, enums) durch diecaseabgedeckt werden und dasdefaultden Fehlerfall (also wider Erwarten und Programmierkunst ein out-of-range) behandelt.Meistens lässt sich das so umsetzen, und so hat man projektweit den Wiedererkennungseffekt
default-> Fehlerfallbehandlung.
Nur in wenigen Fällen kam es bisher vor, dass ich dasdefaulteben wirklich für "alle anderen Fälle" genutzt habe, weil ein if/else wegen der vielen Einzelfälle zu umständlich gewesen wäre.Für das Problem mit dem versehentlichen
if( a = 1 )hab ich mir
if( 1 == a )angewöhnt, dann meckert der Compiler, wenn man = schreibt...
-
minastaros schrieb:
Für das Problem mit dem versehentlichen
if( a = 1 )hab ich mir
if( 1 == a )angewöhnt, dann meckert der Compiler, wenn man = schreibt...
... anstatt die Compilerwarnungen anzuschalten, was der viel bessere Weg gewesen wäre.
-
volkard schrieb:
... anstatt die Compilerwarnungen anzuschalten, was der viel bessere Weg gewesen wäre.
Die sind an
Ist mir nur nicht aufgefallen, dass er da warnt, weil das bei mir ja schon ein Error ist. Gut zu wissen.
-
minastaros schrieb:
Für das Problem mit dem versehentlichen
if( a = 1 )hab ich mir
if( 1 == a )angewöhnt, dann meckert der Compiler, wenn man = schreibt...
Hihi, das hört man immer wieder

Was ich davon halte:- Es ist weniger übersichtlich und logisch, wenn die zu prüfende Variable am Schluss steht. Gerade wenn der Ausdruck komplizierter ist als ein Literal.
- Man kann das nicht konsistent durchziehen. Was machst du bei
a == b? - Man wiegt sich in falscher Sicherheit. Zum Beispiel kann
if (Function() = b)ohne Probleme kompilieren, wenn ein nicht-skalarer RValue zurückgegeben wird. - Dieser typische Anfängerfehler kommt so selten vor, dass es sich nicht lohnt, den Code hässlich zu machen.
- Wie von volkard erwähnt helfen einem gewisse Compiler sogar mit einer Warnung.
-
minastaros schrieb:
volkard schrieb:
... anstatt die Compilerwarnungen anzuschalten, was der viel bessere Weg gewesen wäre.
Die sind an
Ist mir nur nicht aufgefallen, dass er da warnt, weil das bei mir ja schon ein Error ist. Gut zu wissen.Da gibt es wohl nur drei Möglichkeiten:
a) Warnungen nicht ignorieren (empfohlen)
b) auch -Werror einschalten
-
minastaros schrieb:
hab ich mir
if( 1 == a )angewöhnt, dann meckert der Compiler, wenn man = schreibt...
Wenn ich sowas lese verfalle ich in Blutrausch.
Yoda-Programmieren das ist, Reihenfolge in dieser normaler Mensch denkt keiner.
Also warum verdammt sollte man es dann in der Reihenfolge hinschreiben?
*rage*
-
Besten Dank. Auch wenn wir bereits OT diskutieren, noch kurz die Antwort:
@Nexus: Ich seh es eher als Flüchtigkeitsfehler, kann aus Unachtsamkeit immer mal wieder passieren (wenn auch extrem selten).
Alle Fälle erschlägt man mit dem "Trick" nicht, man minimiert zumindest die Fälle mit konstanten Werten.a == b: Genau da würde die Compilerwarnung ja auch jetzt schon greifen. Ist mir halt noch nicht vorgekommen.
@Volkard: Ich sehe Warnungen immer als Fehler an: Keine Freigabe mit Warnungen. Aber zum Experimentieren kann man sie manchmal tolerieren, d.h. kein Werror.
Die Sache ist einfach so: Das hab ich mir das mal irgendwann angewöhnt, und seit dem ist es so drin. Als eine Lösung für die Bedenken von klöklö weiter vorn im Thread würde es zumindest reichen.Insgesamt und spätestens durch Argument "Hässlichkeit" sehe ich die Sache mal wieder als überdenkenswert an.
Blutrausch ... kein normaler Mensch
Hey, locker bleiben, das steht in durchaus guten Büchern als präventive Maßnahme. Es funktionert und ist besser als gar nichts, aber wie gesagt, ich sehe es absolut ein, dass man durch Warnungen (und dann die "richtige" Reihenfolge der Operanden) wesentlich eleganter zum Ziel kommt, und das heißt: sicherer Code.
*tröst*
-
Lesbarkeit zugunsten der Verminderung von Fehleranfälligkeit derjenigen schlechter zu stellen, die noch nicht geübt genug sind Flüchtigkeitsfehler dieser Art nicht mehr zu machen, lässt den Code automatisch als Anfängercode charakterisieren, auch Mal daran gedacht?
Und wenn man sich das angewöhnt, muss man es sich auch erstmal wieder abgewöhnen, die Mühe sollte man sich sparen.
Wenn man geübter ist, sieht man = statt == sofort, auch binnen Sekunden bei einem 50-100-Zeilen-Quelltext. Also besser daran arbeiten, dass man auf diesen Stand kommt. Und wenn man sich vertut, hilft einem Mr. Compiler weiter.

-
minastaros schrieb:
Hey, locker bleiben, das steht in durchaus guten Büchern als präventive Maßnahme.
Die guten C++-Bücher sind alle so modern, daß sie von ordentlichen Compilern ausgehen.
-
Eisflamme schrieb:
Wenn man geübter ist, sieht man = statt == sofort,...
"Defensives Programmieren" geht ja gerade davon aus, dass man auch als Erfahrener eben trotzdem mal was übersehen kann, und sei es ein einziges Mal unter tausenden, und Maßnahmen einzuführen, die das von vorne herein verhindern. Jeder lernt unterschiedlich, man entwickelt sich weiter, und ich z.B. habe meine "Erfahrung" durch den Compilerhinweis heute erweitert.
Übrigens fand ich bislang die Lesbarkeit eines "1 == a" weit weniger störend und den Nutzen größer als z.B. dasZusammenschreibenvonmathematischenAusdrücken, was man so oft sieht.volkard schrieb:
Die guten C++-Bücher sind alle so modern, daß sie von ordentlichen Compilern ausgehen
Das, was ich konkret meine ("Code complete"), sieht Defensives Programmieren zunächst mal sprachunabhängig und diskutiert - in mehreren Programmiersprachen und oder sogar nur Pseudocode - einen ganzen Haufen möglicher Maßnahmen. Vom Compiler ist auf dieser Ebene noch gar nicht die Rede (wie gesagt, ich stimme dem ansonsten ja voll zu).
Ich denke, wir sollten nicht weiter über ein = streiten.
-
minastaros schrieb:
Eisflamme schrieb:
Wenn man geübter ist, sieht man = statt == sofort,...
"Defensives Programmieren" geht ja gerade davon aus, dass man auch als Erfahrener eben trotzdem mal was übersehen kann, und sei es ein einziges Mal unter tausenden, und Maßnahmen einzuführen, die das von vorne herein verhindern. Jeder lernt unterschiedlich, man entwickelt sich weiter, und ich z.B. habe meine "Erfahrung" durch den Compilerhinweis heute erweitert.
Übrigens fand ich bislang die Lesbarkeit eines "1 == a" weit weniger störend und den Nutzen größer als z.B. dasZusammenschreibenvonmathematischenAusdrücken, was man so oft sieht.Du musst den Nutzen von 1 == a mit der verschlechterten Lesbarkeit vergleichen, ich finde das rechnet sich nicht.
Und ich weiß nicht, was Du mit dem Zusammenschreiben von mathematischen Ausdrücken meinst, Du schreibst ja gerade einen Namen auf? Und falls Du lange aber sprechende Funktionsnamen meinst, kann ich entgegnen, dass:
- , wenn die Länge nötig ist, um es sprechend zu schreiben, es für das Verständnis des Codes durch sprechende Namen auch wert ist
- Du es nicht richtig gemacht hast, weil von und mathematischen auch groß geschrieben sein müsste

-
minastaros schrieb:
"Defensives Programmieren" geht ja gerade davon aus, dass man auch als Erfahrener eben trotzdem mal was übersehen kann, und sei es ein einziges Mal unter tausenden, und Maßnahmen einzuführen, die das von vorne herein verhindern.
So kommen wir nicht weiter. Dann müsste man z.B. auch den Code mit irrelevanten
const-Schlüsselwörtern fluten, um zwar die Fehleranfälligkeit einzuschränken, mit ihr aber gleich die Benutzbarkeit, Übersichtlichkeit und Flexibilität. Man dürfte keinenamespace detailmehr verwenden, weil man ja aus Versehen darauf zugreifen könnte. Immer NVI einsetzen, damit man unabsichtlich qualifizierte Aufrufe verhindert. Selbst bei internen Klassen immer allesprivatemachen und mit etlichenfriends oder Zugriffsfunktionen verzieren. Klassen müsste man immer gleich mit virtuellen Destruktoren ausstatten für den Fall, dass sie eines fernen Tages polymorph und mitnewbenutzt werden.Fakt ist, dass du in C++ vieles nicht 100% sicher hinbringst. Die Versuche, es dennoch zu tun, führen selten zum Ziel, aber bringen ungerechtfertigt viele Nachteile und Schein-Sicherheiten mit sich. Das ist ein zu hoher Preis, um einen nie auftretenden Fehler teilweise zu verhindern.