Bedingte Anweisungen Bedingte Anweisungen werden nicht immer ausgeführt, sondern nur, wenn eine Bedingung erfüllt (wahr) ist. C/C++/Java bieten hier 2 Konstruktionen an: if-anweisung sowie switch-case Block. Erstere ist die allgemeinere Variante und wird viel häufiger eingesetzt. Die if-anweisung Syntax: if (Bedingung) // die runden Klammern sind Pflicht! genau-eine-anweisung; oder if (Bedingung) { Eine-Anweisung; Noch-eine-Anweisung;... Die Bedingung ist in C (also auch in C++ gestattet) irgendein numerischer Ausdruck. Ein Wert von 0 gilt als falsch und ein Wert ungleich 0 als wahr. In C++ kann (bzw. soll!) auch ein Ausdruck mit dem Ergebnistyp bool verwendet werden (in Java muss ein Ausdruck mit Ergebnis boolean verwendet werden). Einrücken zur optischen Verdeutlichung der Abhängigkeit gehört zum guten Stil und wird dringend empfohlen. Ich schlage die Einrückung um eine Tabulatorposition (= 4-8 Zeichen) vor, damit die Abhängigkeit klar ersichtlich ist. Kernighan/Ritchie würden die öffnende Klammer auf derselben Zeile wie die Bedingung schreiben. Beispiele: if (1) // ok in C/C++, falsch in Java (kein boolean) cout << "diese Anweisung wird immer ausgeführt\n"; if (true) // C++ und Java cout << "diese Anweisung wird immer ausgeführt\n";
// ab 2 Anweisungen MUSS man Klammern { verwenden { cout << "x ist zu gross und wird durch 5 ersetzt\n"; x = 5; { // K&R Klammernsetzung, ist nur optisch unterschiedlich cout << "x ist zu gross und wird durch 5 ersetzt\n"; x = 5; Fehler: if (i > 5) cout << "i ist zu gross\n"; return EXIT_SUCCESS; // diese Anweisung steht NICHT im if if (i > 5); // der Klassiker: falscher Strichpunkt cout << "i zu gross\n"; // diese Anweisung steht NICHT im if Der Fehler mit dem falschen Strichpunkt wird nicht vom Compiler erkannt, da es sich hierbei um keinen Syntaxfehler handelt. Solche Patzer sind extrem schwer zu finden, da optisch fast alles richtig aussieht. Fehler: if (i = 0) // Achtung: das ist eine Zuweisung, sollte sein: i == 0 cout << "i darf nicht 0 sein!\n"; Solche Fehler passieren in C/C++ oft, weil die Zuweisung (ein Gleichheitszeichen) und die Abfrage auf Gleichheit (logischer Operator ==) so ähnlich sind. Dummerweise ist diese Konstruktion in C/C++ erlaubt, auch wenn sie meistens völlig sinnfrei ist. Der Compiler gibt deshalb nicht einmal eine Warnung aus (der Gnu-Compiler warnt immerhin bei der Option - Wall für alle Warnungen). In Java wäre das fast immer ein Syntaxfehler, da das Ergebnis der Zuweisung meistens kein Wahrheitswert ist.
Der if - if - Block Die if-anweisung kann mit einer -Anweisung kombiniert werden (muss es aber nicht). In diesem Fall muss die -Anweisung direkt auf die if-anweisung folgen. Genauso wie beim if kann man genau eine Anweisung oder aber einen Anweisungsblock zwischen geschweiften Klammer { verwenden. Diese Anweisung(en) kommen genau dann zur Ausführung, wenn die if-bedingung nicht zutrifft (Alternative). Auch hier empfehle ich das Einrücken. Beispiel: Fehler: ; // dieser Strichpunkt ist die leere Anweisung // NICHT im if // Syntaxfehler: folgt nicht direkt auf das if Lässt man vom wieder eine if-anweisung abhängen, so spricht man von einer if Konstruktion. Eine solche gibt es in unseren 3 Sprachen eigentlich nicht, sondern es ist eben eine Kombination von mit einem weiteren if. Man rückt aber anders ein, um Platz zu sparen: schlecht: if (x < -5) // zu weit rechts besser: cout "x ist zu gross\n"; if (x < -5)
Dadurch kann man wirklich von einem if - if - Block sprechen : if (x < -70) cout << "x ist viel zu klein\n"; if (x < -7) Vergleiche obigen Code mit der äquivalenten Variante ohne if if (x < -70) cout << "x ist viel zu klein\n"; { if (x < -7) { Die Bedingungen werden der Reihe nach überprüft. Die erste zutreffende Bedingung bestimmt die auszuführende Anweisung (oder den ganzen Anweisungsblock in {) und beendet den ganzen if-block. Trifft keine der Bedingungen zu und ist ein reines vorhanden, so wird dessen Anweisung ausgeführt. Kommen mehrere if-anweisungen vor und nur ein, so ist es manchmal schwierig zu erkennen, wohin letzteres gehört. In diesem Fall sollte man immer klammern: if (x > 70) cout << x ist viel zu gross\n"; // nicht (x > 70) genau dasselbe, aber optisch eindeutig:
{ if (x > 70) cout << "x ist viel zu gross\n"; die andere Variante (die etwas ganz anderes bewirkt): hier muss man Klammern setzen: { if (x > 70) cout << "x ist viel zu gross\n"; // nicht (x > 7) cout << "x ist ok\n"; Ich möchte noch einmal betonen, dass das korrekte Einrücken nur für den Programmierer wichtig ist. Der Compiler ignoriert alle Arten von Einrückungen bei der Übersetzung. if und C++17 Seit C++17 ist eine weitere Form des if-statements erlaubt: if (Init; Bedingung) genau-eine-anweisung; Init ist meist eine Variablendefinition. Solche Variablen stehen im gesamten if-block und auch (falls vorhanden) im -Block zur Verfügung, sind aber außerhalb des if nicht mehr definiert: if (double d = b*b 4*a*c; d > 0) // 2 reelle Loesungen if (d == 0) // reelle Doppel-Loesung // Diskriminante negativ // 2 konjugiert-komplexe Loesungen // Diskriminante positiv Der switch-case Block Manchmal muss man verschiedene (ganzzahlige!) Werte eines Ausdrucks oder einer Variablen untersuchen. Wir wollen z.b. die Werte der Variablen errno (Fehlernummer)
abfragen und je nach Wert die richtige Fehlermeldung ausgeben. Mit einem if-block ginge das so: if (errno == 1) // Abfrage auf Gleichheit cout << "Datei wurde nicht gefunden\n"; if (errno == 5) cout << "Ausgabedatei konnte nicht geschrieben werden\n";... cout << "unbekannte Fehlernummer: " << errno << '\n'; Obwohl dieses Beispiel nicht ideal für die Demonstration ist, verwende ich dafür den switch-case Block: switch (errno) { // () um den Wert und geschweifte Klammern { sind Pflicht case 1: // hier braucht man keine Klammern cout << "Datei wurde nicht gefunden\n"; // beendet diesen Fall case 5: cout << "Ausgabedatei konnte nicht geschrieben werden\n";... default: // entspricht dem cout << "unbekannte Fehlernummer: " << errno << '\n'; Im Allgemeinen erzeugt der Compiler bei vielen zu untersuchenden Fällen wesentlich schnelleren Code als bei der if-variante. Will man für 2 unterschiedliche Werte dieselben Statements ausführen lassen, schreibt man einfach zwei case-statements untereinander: case 1: case 5: // in beiden Fällen mache das folgende cout << "Datei wurde nicht gefunden\n"; Beachte: Trifft ein Fall zu, so endet dessen Anweisungsblock erst bei der nächsten break- Anweisung! Vergisst man diese, so werden u.u. zu viele Anweisungen ausgeführt.
Ein besseres Beispiel: In der Variable monat sei eine Monatsnummer gespeichert und man möchte die Variable tage mit der richtigen Anzahl von Wochentagen für dieses Monat belegen. Hier die Lösung mit switch-case (ohne Schaltjahre!): switch(monat) { case 1: // Jänner case 3: // März case 5: // Mai case 7: // Juli case 8: // August case 10: // Oktober case 12: // Dezember tage = 31; // beendet diesen Fall case 2: // Feber tage = 28; // beendet diesen Fall case 4: // April case 6: // Juni case 9: // September case 11: // November tage = 30; // beendet diesen Fall default: // entspricht dem bei einem if cout << "falscher Monat in monat: " << monat << '\n'; Der Fall default ist optional, d.h. man muss ihn nicht verwenden. Auch die Reihenfolge ist egal (default muss nicht der letzte Fall sein). Die break Anweisung ist nur im switch-block und in Schleifen erlaubt, aber nicht in if-konstruktionen. Eine return Anweisung beendet immer die ganze Funktion, d.h. insbesondere auch einen switch- Block oder einen if-block. Seit C++17 ist auch die Form switch (Init; Ausdruck) möglich