C++-Programmierung/ Einführung in C++/ Verzweigungen
Eine Verzweigung (bedingte Anweisung, conditional statement) dient dazu, ein Programm in mehrere Pfade aufzuteilen. Beispielsweise kann so auf Eingaben des Benutzers reagiert werden. Je nachdem, was der Benutzer eingibt, ändert sich der Programmablauf.
Falls
[Bearbeiten]Verzweigungen werden mit dem Schlüsselwort if
begonnen. In der einfachsten Form sieht das so aus:
Wenn die Bedingung
erfüllt ist, wird die Anweisung
ausgeführt, ansonsten wird sie übersprungen. Sollen nicht nur eine, sondern mehrere Anweisungen ausgeführt werden, fassen Sie diese mit {...}
zu einer Blockanweisung zusammen:
Als Bedingung
darf jeder Ausdruck verwendet werden, der einen bool
zurückgibt oder dessen Ergebnis sich in einen bool
umwandeln lässt. Ganzzahlige und Gleitkommadatentypen lassen sich nach bool
umwandeln, die Regel lautet: Ist eine Zahl (exakt) gleich 0, so wird sie als false
ausgewertet, andernfalls als true
.
Andernfalls
[Bearbeiten]Das Schlüsselwort else
erweitert die Einsatzmöglichkeiten der Verzweigung. Während ein normales (also einzelnes) if
einen bestimmten Teil des Codes ausführt, falls eine Bedingung erfüllt ist, stellt else
eine Erweiterung dar, anderen Code auszuführen, falls die Bedingung nicht erfüllt ist.
int i;
cin >> i;
if (i)
cout << "Sie haben einen Wert ungleich 0 eingegeben!\n";
else
cout << "Sie haben 0 eingegeben!\n";
Natürlich könnten auch hier, sowohl für die if
-Anweisung, als auch für die else
-Anweisung, ein Anweisungsblock stehen. Wenn Sie Pascal oder eine ähnliche Programmiersprache kennen, wird Ihnen auffallen, dass auch die Anweisung vor dem else
mit einem Semikolon abgeschlossen wird. Da auf eine if
- oder else
-Anweisung immer nur eine Anweisung oder ein Anweisungsblock stehen kann, muss zwangsläufig direkt danach ein else
stehen, um dem if
zugeordnet zu werden.
Sie können in einer Verzweigungsanweisung auch mehr als zwei Alternativen angeben:
int i;
cin >> i;
if (i == 10)
cout << "Sie haben zehn eingegeben\n";
else
if (i == 11)
cout << "Sie haben elf eingegeben\n";
else
cout << "Sie haben weder zehn noch elf eingegeben\n";
Es können beliebig viele Zweige mit else if
vorkommen. Allerdings ist es üblich, eine andere Einrückung zu wählen, wenn solche „if
-else
-Bäume“ ausgebaut werden:
int i;
cin >> i;
if (i == 10)
cout << "Sie haben zehn eingegeben\n";
else if (i == 11)
cout << "Sie haben elf eingegeben\n";
else
cout << "Sie haben weder zehn noch elf eingegeben\n";
Außerdem ist es zu empfehlen, auch bei einer Anweisung einen Anweisungsblock zu benutzen. Letztlich ist die Funktionalität immer die gleiche, aber solche Blöcke erhöhen die Übersichtlichkeit und wenn Sie später mehrere Anweisungen, statt nur einer angeben möchten, brauchen Sie sich um etwaige Klammern keine Gedanken zu machen, weil sie sowieso schon vorhanden sind.
int i;
cin >> i;
if (i == 10) {
cout << "Sie haben zehn eingegeben\n";
} else if(i == 11) {
cout << "Sie haben elf eingegeben\n";
} else {
cout << "Sie haben weder zehn noch elf eingegeben\n";
}
Sie werden für die Positionierung der Klammern übrigens auch oft auf eine andere Variante treffen:
int i;
cin >> i;
if (i == 10)
{
cout << "Sie haben zehn eingegeben\n";
}
else
{
if (i == 11)
{
cout << "Sie haben elf eingegeben\n";
}
else
{
cout << "Sie haben weder zehn noch elf eingegeben\n";
}
}
Einige Programmierer finden dies übersichtlicher, für dieses Buch wurde jedoch die Variante mit den öffnenden Klammern ohne Extrazeile verwendet. Das hat den Vorteil, dass weniger Platz benötigt wird und da die Einrückung ohnehin die Zugehörigkeit andeutet, ist eine zusätzliche Kennzeichnung nicht unbedingt nötig.
Die Einrückung von Quelltextzeilen hat für den Compiler übrigens keine Bedeutung. Sie ist lediglich eine grafische Darstellungshilfe für den Programmierer. Auch die in diesem Buch gewählte Einrückungstiefe von vier Leerzeichen ist optional, viele Programmierer verwenden etwa nur zwei Leerzeichen. Andere hingegen sind davon überzeugt, dass acht die ideale Wahl ist. Aber egal, wofür Sie sich entscheiden, wichtig ist, dass Sie Ihren Stil einhalten und nicht ständig ihren Stil wechseln. Das verwirrt nicht nur, sondern sieht auch nicht schön aus.
Wenn es Sie nicht stört, in Ihrem Texteditor Tabulatorzeichen und Leerzeichen anzeigen zu lassen, dann sollten Sie für die Einrückung Tabulatorzeichen verwenden und für alles hinter der normalen Einrückung (etwa den Abstand bis zum Kommentar) Leerzeichen. Das hat den Vorteil, dass Sie die Einrückungstiefe jederzeit ändern können, indem Sie angeben wie viele Leerzeichen einem Tabulatorzeichen entsprechen.
Nachteil: Wenn Sie den Quelltext in verschiedenen Editoren bearbeiten oder weitergeben, muss in jedem Editor eingestellt werden, was die Tabulator-Breite sein soll. Beim Verwenden von Leerzeichen bleibt die Einrückung immer gleich, auch wenn die Tabulatorbreite verschieden eingestellt sein sollte.
Vergleichsoperatoren
[Bearbeiten]Im obigen Beispiel kam schon der Vergleichsoperator ==
zum Einsatz. In C++ gibt es insgesamt sechs Vergleichsoperatoren. Sie liefern jeweils den Wert true
, wenn die beiden Operanden (die links und rechts des Operators stehen) dem Vergleichskriterium genügen, ansonsten den Wert false
.
==
|
identisch |
<=
|
ist kleiner (oder) gleich |
>=
|
ist größer (oder) gleich |
<
|
ist kleiner |
>
|
ist größer |
!=
|
ist ungleich |
Der Vergleichsoperator ==
wird von Anfängern oft mit dem Zuweisungsoperator =
verwechselt. Da es absolut legal ist, eine Zuweisung innerhalb einer if
-Bedingung zu machen, führt das oft zu schwer zu findenden Fehlern. Eine Zuweisung wird ausgewertet zum zugewiesenen Wert.
Problem-Beispiel:
Die „Bedingung“ weist den Wert von b
an die Variable a
zu (a = 8
). Für das if
wird der Gesamtausdruck ausgewertet (also 8
), was true
bedeutet.
Prüfen Sie bei seltsamen Verhalten also immer, ob vielleicht der Zuweisungsoperator =
statt des Gleichheitsoperators ==
verwendet wurde.
Eine weitere Falle ist der Ungleichheitsoperator !=
, wenn er falsch herum geschrieben wird (=!
). Letzteres sind in Wahrheit zwei Operatoren, nämlich die Zuweisung =
und die logische Negierung !
, die Sie gleich kennen lernen werden. Um das zu unterscheiden, machen Sie sich einfach klar, was das in Worten heißt:
!=
– nicht gleich=!
– gleich nicht
Logische Operatoren
[Bearbeiten]Mit logischen Operatoren können Sie mehrere Bedingungen zu einem Ausdruck verknüpfen. C++ bietet folgende Möglichkeiten:
!
|
Logisches Nicht | Resultat wahr, wenn der Operand falsch ist |
&&
|
Logisches Und | Resultat wahr, wenn beide Operanden wahr sind |
||
|
Logisches Oder | Resultat wahr, wenn mindestens ein Operand wahr ist (inclusive-or) |
Die Operatoren lassen sich übersichtlich mit Wahrheitstafeln beschreiben (bei der hier gewählten Darstellung ist jede Spalte für sich zu lesen):
Logisches Und (&& )
| ||||
---|---|---|---|---|
a |
true |
true |
false |
false
|
b |
true |
false |
true |
false
|
a && b |
true |
false |
false |
false
|
Beispiel für die dritte Spalte: Mit a = false
und b = true
gilt a && b -> false
.
Logisches Oder (|| )
| ||||
---|---|---|---|---|
a |
true |
true |
false |
false
|
b |
true |
false |
true |
false
|
a || b |
true |
true |
true |
false
|
Logisches Nicht (! )
| ||
---|---|---|
a |
true |
false
|
!a |
false |
true
|
Beispiel:
Aus Gründen der Lesbarkeit sollten Vergleichsausdrücke grundsätzlich von Klammern umgeben sein. Der obige Code würde folglich so aussehen:
Sowohl beim &&
-Operatoren (Logik-und) als auch beim ||
-Operator (Logik-oder) werden die Teilausdrücke von links nach rechts bewertet, und zwar nur so lange, bis das Resultat feststeht. Wenn z. B. bei einer &&
-Verknüpfung A && B && C
schon die erste Bedingung 'A' falsch ist, werden 'B' und 'C' gar nicht mehr untersucht, da bei &&
ja alle Bedingungen true
sein müssen.
Der Rückgabewert der beiden Operatoren ist vom Typ bool
.
Gelegentlich ist daher anzutreffen:
if ( (variable_ist_gueltig) && (variable_erfuellt_zusaetzliche_detailbedingung) ) { /* mache etwas */ }
Ob die Variable die Detailbedingung erfüllt, kann nur geprüft werden, wenn sie einen gültigen Wert enthält. Die erste Bedingung auf Gültigkeit schützt somit die nachfolgende davor, mit ungültigen Werten arbeiten zu müssen.
Beachten Sie bitte, dass der UND-Operator (&&
) eine höhere Priorität als der ODER-Operator (||
) hat. Das heißt, Sie müssen bei Ausdrücken wie dem Folgenden vorsichtig sein.
int i = 10, j = 20;
// Erwartete Reihenfolge ((((i == 10) || (j == 20)) && (j == 20)) && (i == 5))
// Tatsächliche Reihenfolge ((i == 10) || (((j == 20) && (j == 20)) && (i == 5)))
if (i == 10 || j == 20 && j == 20 && i == 5) {
cout << "i ist Zehn und fünf oder (j ist Zwanzig und i fünf)!\n";
} else {
cout << "i ist nicht Zehn oder (j ist Zwanzig oder i nicht fünf)!\n";
}
i ist Zehn und fünf oder (j ist Zwanzig und i fünf)!
Die Ausgabe ist von der Logik her falsch, weil der Ausdruck in der Reihenfolge ((i == 10) || (((j == 20) && (j == 20)) && (i == 5)))
ausgewertet wird. Solche Fehler sind sehr schwer zu finden, also sollten Sie sie auch nicht machen. Daher der Tipp: Verwenden Sie bei solch komplexen Bedingungen immer Klammern, um klar zu machen, in welcher Reihenfolge Sie die Ausdrücke auswerten wollen. Das ist unmissverständlich, und der menschliche Leser liest den Ausdruck genauso wie der Compiler. Um zu erkennen, an welcher Stelle eine Klammer wieder geschlossen wird, beherrschen die meisten Editoren das sogenannte Bracket Matching. Dabei hebt der Editor (automatisch oder über einen bestimmten Hotkey) die schließende Klammer hervor.
Wenn Sie mit mehreren &&
und ||
arbeiten, dann schreiben Sie den Ausdruck, der am wahrscheinlichsten zutrifft, auch am weitesten links, also vor den anderen Ausdrücken. Das ist eine (zugegebenermaßen) sehr geringfügige Optimierung, aber es gibt Situationen in denen sie trotzdem sinnvoll ist. Beispielsweise wenn die Bedingung innerhalb einer Schleife sehr oft ausgeführt wird.
Hinweis für fortgeschrittene Leser: Beachten Sie bitte, dass für überladende Operatoren andere Regeln gelten. Alle diejenigen, die noch nicht wissen, was überladende Operatoren sind, brauchen sich um diesen Hinweis (noch) nicht zu kümmern.
Bedingter Ausdruck
[Bearbeiten]Häufig werden Verzweigungen eingesetzt, um abhängig vom Wert eines Ausdrucks eine Zuweisung vorzunehmen. Das können Sie mit dem Auswahloperator ? ... : ...
auch einfacher formulieren:
Grafisch sieht das so aus:
Der Variablen min
wird der kleinere, der beiden Werte a
und b
zugewiesen. Analog zum Verhalten der logischen Operatoren wird nur derjenige „Zweig“ bewertet, der nach Auswertung der Bedingung (a < b
) tatsächlich ausgeführt wird.
Der Bedingungsoperator kann auch wie folgt verwendet werden:
Im Gegensatz zur if...else
-Anweisung ist es hier aber nicht möglich, in Abhängigkeit zu einer Bedingung,
nur eine Anweisung auszugeben, indem man die else
-Anweisung einfach weg lässt:
Das liegt daran, dass der Bedingungsoperator ?:
keine Kontrollstruktur im eigentlichen Sinne ist.
Vielmehr wird mit (a) ? make(b) : make(c);
ein Ausdruck „berechnet“, der einen Wert aufweisen muss,
während sich die if
-Anweisung logisch in einen "Tu-nichts-Pfad" auflösen kann.