Ein Programm ist eigentlich nichts weiter als eine Folge von Befehlen, die nacheinander ausgeführt werden. Mächtig wird ein Programm erst dadurch, daß es in Abhängigkeit von einer Bedingung entscheiden kann, ob der eine oder ein anderer Anweisungsblock ausgeführt werden soll.
In C++ steuern Anweisungen die Reihenfolge der Ausführung, werten Ausdrücke aus
oder bewirken nichts (die Leeranweisung). Alle C++-Anweisungen enden mit einem
Semikolon (;
), auch die Leeranweisung, die nur aus einem Semikolon besteht.
Eine häufig gebrauchte einfache Anweisung ist die Zuweisung:
x = a + b;
Diese Anweisung bedeutet im Gegensatz zur Algebra nicht x
gleich a + b
, sondern ist
wie folgt zu interpretieren: »Weise den Wert der Summe aus a
und b
an x
zu.« Auch
wenn diese Anweisung zwei Dinge bewirkt, handelt es sich um eine einzige Anweisung
und hat daher nur ein Semikolon. Der Zuweisungsoperator nimmt die Zuweisung
des auf der rechten Seite stehenden Ausdrucks an die linke Seite vor.
Leerzeichen gehören zusammen mit Tabulatoren und den Zeilenvorschüben zu den sogenannten Whitespace-Zeichen. Sie werden im allgemeinen in den Anweisungen ignoriert. Die oben behandelte Zuweisung läßt sich auch wie folgt schreiben:
x=a+b;
x =a
+ b ;
Die zweite Variante ist zwar zulässig, aber kompletter Blödsinn. Durch Whitespace- Zeichen sollen Programme leichter zu lesen und zu warten sein. Man kann damit aber auch einen unleserlichen Code produzieren. C++ stellt die Möglichkeiten bereit, für den sinnvollen Einsatz ist der Programmierer verantwortlich.
Whitespace-Zeichen (Leerzeichen, Tabulatoren und Zeilenvorschübe) sind nicht sichtbar. Werden diese Zeichen gedruckt, bleibt das Papier weiß (white).
An allen Stellen, wo eine einzelne Anweisung stehen kann, ist auch eine Verbundanweisung
(auch Block genannt) zulässig. Ein Block beginnt mit einer öffnenden geschweiften
Klammer ({
) und endet mit einer schließenden geschweiften Klammer (}
).
In einem Block ist zwar jede Anweisung mit einem Semikolon abzuschließen, der Block selbst endet aber nicht mit einem Semikolon. Dazu ein Beispiel:
{
temp = a;
a = b;
b = temp;
}
Dieser Codeblock tauscht die Werte der Variablen in a
und b
aus.
Alles, was zu einem Wert ausgewertet werden kann, nennt man in C++ einen Ausdruck
. Von einem Ausdruck sagt man, daß er einen Wert zurückliefert. Demzufolge ist
die Anweisung 3+2;
, die den Wert 5
zurückliefert, ein Ausdruck. Alle Ausdrücke sind
Anweisungen.
Die Unzahl der Codeabschnitte, die sich als Ausdruck entpuppen, mag Sie vielleicht überraschen. Hier drei Beispiele:
3.2 // liefert den Wert 3.2
PI // float-Konstante, die den Wert 3.14 zurückgibt
SekundenProMinute // int-Konstante, die 60 liefert
Vorausgesetzt, daß PI
eine Konstante mit dem Wert 3.14
und SekundenProMinute
eine
Konstante mit dem Wert 60
ist, stellen alle drei Anweisungen gültige Ausdrücke dar.
x = a + b;
addiert nicht nur a
und b
und weist das Ergebnis an x
zu, sondern liefert auch den
Wert dieser Zuweisung (den Wert in x
). Daher ist diese Anweisung ebenfalls ein Ausdruck
und kann somit auch auf der rechten Seite eines Zuweisungsoperators stehen:
y = x = a + b;
Diese Zeile wird in der folgenden Reihenfolge ausgewertet:
Wenn a
, b
, x
und y
ganze Zahlen sind, a
den Wert 2 und b
den Wert 5 hat, enthält sowohl
x
als auch y
nach Ausführung dieser Anweisung den Wert 7.
Listing 4.1 demonstriert die Auswertung komplexer Ausdrücke.
Listing 4.1: Auswertung komplexer Ausdrücke
1: #include <iostream.h>
2: int main()
3: {
4: int a=0, b=0, x=0, y=35;
5: cout << "a: " << a << " b: " << b;
6: cout << " x: " << x << " y: " << y << endl;
7: a = 9;
8: b = 7;
9: y = x = a+b;
10: cout << "a: " << a << " b: " << b;
11: cout << " x: " << x << " y: " << y << endl;
12: return 0;
13: }
a: 0 b: 0 x: 0 y: 35
a: 9 b: 7 x: 16 y: 16
Zeile 4 deklariert und initialisiert die vier Variablen. Die Ausgabe ihrer Werte erfolgt in
den Zeilen 5 und 6. Zeile 7 weist den Wert 9 an die Variable a
zu. Zeile 8 weist den
Wert 7 an die Variable b
zu. Zeile 9 summiert die Werte von a
und b
und weist das Ergebnis
x
zu. Dieser Ausdruck (x = a+b
) ergibt einen Wert (die Summe aus a
und b
), der
wiederum y
zugewiesen wird.
Ein Operator ist ein Symbol, das den Compiler zur Ausführung einer Aktion veranlaßt. Operatoren verarbeiten Operanden, und in C++ sind alle Operanden Ausdrücke. In C++ gibt es mehrere Arten von Operatoren. Zwei Arten von Operatoren sind:
Der Zuweisungsoperator (=
) bewirkt, daß der Operand auf der linken Seite des Operators
den Wert von der rechten Seite des Operators erhält. Der Ausdruck
x = a + b;
weist dem Operanden x
den Wert zu, der als Ergebnis der Addition von a
und b
entsteht.
Einen Operanden, der auf der linken Seite eines Zuweisungsoperators zulässig ist, bezeichnet man als L-Wert (linker Wert). Ein entsprechender Operand auf der rechten Seite heißt R-Wert.
Konstanten sind R-Werte und können nicht als L-Werte vorkommen. Demzufolge ist die Anweisung
x = 35; // OK
zulässig, während die Anweisung
35 = x; // Fehler, kein L-Wert!
Ein L-Wert ist ein Operand, der auf der linken Seite eines Ausdrucks stehen kann. Als
R-Wert bezeichnet man einen Operanden, der auf der rechten Seite eines Ausdrucks
vorkommen kann. Während alle L-Werte auch als R-Werte zulässig sind, dürfen nicht
alle R-Werte auch als L-Werte verwendet werden. Ein Literal ist zum Beispiel ein R-
Wert, der nicht als L-Wert erlaubt ist. Demzufolge kann man x = 5;
schreiben, 5 = x;
jedoch nicht (x
kann ein R- und eine L-Wert sein, 5
hingegen ist nur ein R-Wert).
C++ kennt die fünf mathematischen Operatoren Addition (+
), Subtraktion (-
), Multiplikation
(*
), Division (/
) und Modulo-Division (%
).
Die zwei Grundrechenarten Addition und Subtraktion arbeiten wie gewohnt, wenn es
auch bei der Subtraktion mit vorzeichenlosen Ganzzahlen zu überraschenden Ergebnissen
kommen kann, wenn das Ergebnis eigentlich negativ ist. Einen Eindruck davon
haben Sie schon gestern erhalten, als der Variablenüberlauf beschrieben wurde. In Listing
4.2 können Sie nachvollziehen, was passiert, wenn Sie eine große Zahl eines unsigned
-Typs von einer kleinen Zahl eines unsigned
-Typs subtrahieren.
Listing 4.2: Subtraktion und Integer-Überlauf
1: // Listing 4.2 - Subtraktion und
2: // Integer-Überlauf
3: #include <iostream.h>
4:
5: int main()
6: {
7: unsigned int difference;
8: unsigned int bigNumber = 100;
9: unsigned int smallNumber = 50;
10: difference = bigNumber - smallNumber;
11: cout << "Die Differenz beträgt: " << difference;
12: difference = smallNumber - bigNumber;
13: cout << "\nJetzt beträgt die Differenz: " << difference <<endl;
14: return 0;
15: }
Die Differenz beträgt: 50
Jetzt beträgt die Differenz: 4294967246
Zeile 10 ruft den Subtraktions-Operator auf, und das Ergebnis, das ganz unseren Erwartungen
entspricht, wird in Zeile 11 ausgegeben. Zeile 12 ruft den Subtraktions-
Operator erneut auf. Diesmal wird jedoch eine große unsigned
-Zahl von einer kleinen
unsigned
-Zahl subtrahiert. Das Ergebnis wäre eigentlich negativ, doch da es als unsigned
-Zahl ausgewertet (und ausgegeben) wird, ist das Ergebnis, wie gestern beschrieben,
ein Überlauf. Auf dieses Problem wird noch genauer in Anhang A »Operator-
Vorrang« eingegangen.
Die Integer-Division unterscheidet sich etwas von der gewohnten Division. Um genau
zu sein: Integer-Division entspricht dem, was Sie in der zweiten Klasse gelernt haben.
Dividiert man 21
durch 4
und betrachtet das Ganze als Integer-Division (wie in der
Grundschule mit sieben Jahren) ist das Ergebnis 5
Rest 1
.
Um den Rest zu erhalten, bedienen Sie sich des Modulo-Operators (%
) und berechnen
21
modulus 4
(21 % 4
) mit dem Ergebnis 1. Der Modulo-Operator gibt den Rest einer
Ganzzahldivision zurück.
Die Berechnung des Modulus kann recht nützlich sein, wenn Sie zum Beispiel eine
Anweisung bei jeder zehnten Aktion drucken wollen. Jede Zahl, deren Modulus 0
ergibt,
wenn Sie deren Modulus mit 10
berechnen, ist ein Mehrfaches von 10
. So ist 1 %
10
gleich 1
, 2 % 10
gleich 2
und so weiter, bis 10 % 10
den Wert 0
ergibt. 11 % 10
ist erneut
1
und dieses Muster setzt sich bis zum nächsten Mehrfachen von 10
, das heißt 20
,
fort. Diese Technik kommt zur Anwendung, wenn wir am Tag 7 auf die Schleifen zu
sprechen kommen.
Wenn ich
5
durch3
teile, erhalte ich1
. Was mache ich falsch?Antwort: Wenn Sie einen Integer durch einen anderen teilen, erhalten Sie auch einen Integer als Ergebnis. Und im Falle von
5/3
ist dies eben1
.Um als Rückgabewert eine Bruchzahl zu erhalten, müssen Sie Fließkommazahlen vom Typ
float
verwenden.
5,0/3,0
ergibt als Bruchzahl:1,66667
Falls Ihre Methode Integer als Parameter übernimmt, müssen Sie diese in Fließkommazahlen von Typ
float
umwandeln.Um den Typ der Variablen zu ändern, müssen Sie eine Typumwandlung (
cast
) vornehmen. Damit teilen Sie dem Compiler im wesentlichen mit, daß Sie wissen was Sie tun. Und das sollten Sie auch, denn der Compiler wird sich Ihrem Willen nicht widersetzen.In diesem speziellen Fall teilen Sie dem Compiler mit: »Ich weiß, du denkst, dies ist ein Integer, aber ich weiß, was ich tue, darum behandle diesen Wert als Fließkommazahl«.
Für die Typumwandlung können Sie noch die alte C-Methode oder den neuen vom ANSI-Standard empfohlenen Operator
static_cast
verwenden. Ein Beispiel dazu finden Sie in Listing 4.3.
Listing 4.3: Typumwandlung in einen Float
1: #include <iostream.h>
2:
3: void intDiv(int x, int y)
4: {
5: int z = x / y;
6: cout << "z: " << z << endl;
7: }
8:
9: void floatDiv(int x, int y)
10:{
11: float a = (float)x; // alter Stil
12: float b = static_cast<float>(y); // vorzuziehender Stil
13: float c = a / b;
14:
15: cout << "c: " << c << endl;
16:}
17:
18: int main()
19: {
20: int x = 5, y = 3;
21: intDiv(x,y);
22: floatDiv(x,y);
23: return 0;
24: }
Z: 1
C: 1.66667
Zeile 20 deklariert zwei Integer-Variablen. Als Parameter werden in Zeile 21 intDiv
und in Zeile 22 floatDiv
übergeben. Die zweite Methode beginnt, wie man sieht, in
Zeile 9. Die Typumwandlung der Integer-Variablen zu Variablen vom Typ float
erfolgt
in den Zeilen 11 und 12. Das Ergebnis der Division wird in Zeile 13 einer dritten
float
-Variablen zugewiesen und in Zeile 15 ausgegeben.
Häufig muß man einen Wert zu einer Variablen addieren und dann das Ergebnis an
dieselbe Variable zuweisen. Im folgenden Beispiel wird der Wert der Variablen meinAlter
um 2 erhöht:
int meinAlter = 5;
int temp;
temp = meinAlter + 2; // 5 und 2 addieren und in temp ablegen
meinAlter = temp; // nach meinAlter zurückschreiben
Dieses Verfahren ist allerdings recht umständlich. In C++ kann man dieselbe Variable auf beiden Seiten des Zuweisungsoperators einsetzen und das obige Codefragment eleganter formulieren:
meinAlter = meinAlter +2;
In der Algebra wäre dieser Ausdruck unzulässig, während ihn C++ als »addiere 2 zum
Wert in meinAlter
, und weise das Ergebnis an meinAlter
zu« interpretiert.
Das Ganze läßt sich noch einfacher schreiben, auch wenn es im ersten Moment etwas unverständlich aussieht:
meinAlter += 2;
Der zusammengesetzte Additionsoperator (+=
) addiert den R-Wert zum L-Wert und
führt dann eine erneute Zuweisung des Ergebnisses an den L-Wert durch. Der Operator
heißt »Plus-Gleich«. Die Anweisung ist dann als »meinAlter
plus gleich 2« zu lesen.
Wenn meinAlter
zu Beginn den Wert 4 enthält, steht nach Ausführung dieser Anweisung
der Wert 6 in meinAlter
.
Weitere zusammengesetzte Operatoren gibt es für Subtraktion (-=
), Division (/=
), Multiplikation
(*=
) und Modulo-Operation (%=
).
Am häufigsten hat man den Wert 1 zu einer Variablen zu addieren (bzw. zu subtrahieren). In C++ spricht man beim Erhöhen des Wertes um 1 von Inkrementieren und beim Verringern um 1 von Dekrementieren. Für diese Operationen stehen spezielle Operatoren bereit.
Der Inkrement-Operator (++
) erhöht den Wert der Variablen um 1, der Dekrement-
Operator (--
) verringert ihn um 1. Möchte man die Variable C inkrementieren,
schreibt man die folgende Anweisung:
C++; // Beginne mit C und inkrementiere den enthaltenen Wert.
Diese Anweisung ist äquivalent zur ausführlicher geschriebenen Anweisung
C = C + 1;
Das gleiche Ergebnis liefert die verkürzte Darstellung
C += 1;
Sowohl der Inkrement-Operator (++
) als auch der Dekrement-Operator (--
) existieren
in zwei Spielarten: Präfix und Postfix. Die Präfix-Version wird vor den Variablennamen
geschrieben (++mein Alter
), die Postfix-Version danach (mein Alter++
).
In einer einfachen Anweisung spielt es keine Rolle, welche Version man verwendet. In einer komplexen Anweisung ist es allerdings entscheidend, ob man eine Variable zuerst inkrementiert (oder dekrementiert) und dann das Ergebnis einer anderen Variablen zuweist. Der Präfix-Operator wird vor der Zuweisung ausgewertet, der Postfix- Operator nach der Zuweisung.
Die Semantik von Präfix bedeutet: Inkrementiere den Wert und übertrage ihn dann. Die Bedeutung des Postfix-Operators lautet dagegen: Übertrage den Wert und inkrementiere das Original.
Das folgende Beispiel verdeutlicht diese Vorgänge. Es sei angenommen, daß x
eine
ganze Zahl mit dem Wert 5 ist. Bei der Anweisung
int a = ++x;
inkrementiert der Compiler den Wert in x
(und macht ihn damit zu 6), holt dann diesen
Wert und weist ihn an a
zu. Daher ist jetzt sowohl a
als auch x
gleich 6.
int b = x++;
weist man den Compiler an, den Wert in x
(6) zu holen, ihn an b
zuzuweisen und dann
den Wert von x
zu inkrementieren. Demzufolge ist nun b
gleich 6, während x
gleich 7
ist. Listing 4.4 zeigt Verwendung und Besonderheiten beider Typen.
Listing 4.4: Präfix- und Postfix-Operatoren
1: // Listing 4.4 - zeigt die Verwendung der
2: // Inkrement- und Dekrement-Operatoren in
3: // Präfix- und Postfix-Notation
4: #include <iostream.h>
5: int main()
6: {
7: int mein Alter = 39; // initialisiert zwei Integer-Variablen
8: int dein Alter = 39;
9: cout << "Ich bin: " << mein Alter << " Jahre alt.\n";
10: cout << "Du bist: " << dein Alter << " Jahre alt\n";
11: mein Alter++; // Postfix-Inkrement
12: ++dein Alter; // Präfix-Inkrement
13: cout << "Ein Jahr ist vergangen...\n";
14: cout << "Ich bin: " << mein Alter << " Jahre alt.\n";
15: cout << "Du bist: " << dein Alter << " Jahre alt\n";
16: cout << "Noch ein Jahr ist vergangen\n";
17: cout << "Ich bin: " << mein Alter++ << " Jahre alt.\n";
18: cout << "Du bist: " << ++dein Alter << " Jahre alt\n";
19: cout << "Und noch einmal ausgeben.\n";
20: cout << "Ich bin: " << mein Alter << " Jahre alt.\n";
21: cout << "Du bist: " << dein Alter << " Jahre alt\n";
22: return 0;
23: }
Ich bin: 39 Jahre alt.
Du bist: 39 Jahre alt
Ein Jahr ist vergangen...
Ich bin: 40 Jahre alt.
Du bist: 40 Jahre alt
Noch ein Jahr ist vergangen
Ich bin: 40 Jahre alt.
Du bist: 41 Jahre alt
Und noch einmal ausgeben.
Ich bin: 41 Jahre alt.
Du bist: 41 Jahre alt
Die Zeilen 7 und 8 deklarieren zwei Integer-Variablen und initialisieren sie jeweils mit dem Wert 39. Die Ausgabe der Werte findet in den Zeilen 9 und 10 statt.
Zeile 11 inkrementiert mein Alter
mit dem Postfix-Operator, und Zeile 12 inkrementiert
dein Alter
mit dem Präfix-Operator. Die Ergebnisse werden in den Zeilen 14
und 15 ausgegeben und sind beide identisch (beide 40).
In Zeile 17 wird mein Alter
mit dem Postfix-Operator als Teil einer Ausgabeanweisung
inkrementiert. Durch den Postfix-Operator erfolgt die Inkrementierung nach der
Ausgabe, und es erscheint auch hier in der Ausgabe der Wert 40. Im Gegensatz dazu
inkrementiert Zeile 18 die Variable dein Alter
mit dem Präfix-Operator. Das Inkrementieren
findet jetzt vor der Ausgabe statt, und es erscheint der Wert 41 in der Anzeige.
Schließlich werden die Werte in den Zeilen 20 und 21 erneut ausgegeben. Da die Inkrement-Anweisung
vollständig abgearbeitet ist, lautet der Wert in mein Alter
jetzt 41,
genau wie der Wert in dein Alter
.
Welche Operation wird in der komplexen Anweisung
x = 5 + 3 * 8;
zuerst ausgeführt, die Addition oder die Multiplikation? Führt man die Addition zuerst aus, lautet das Ergebnis 8 * 8 oder 64. Bei vorrangiger Multiplikation heißt das Ergebnis 5 + 24 oder 29.
Jeder Operator besitzt einen festgelegten Vorrang. Eine vollständige Liste der Operatorprioritäten finden Sie im Anhang A. Die Multiplikation hat gegenüber der Addition höhere Priorität. Der Wert des Ausdrucks ist demnach 29.
Wenn zwei mathematische Operatoren gleichrangig sind, werden sie in der Reihenfolge von links nach rechts ausgeführt. Demzufolge wird in
x = 5 + 3 + 8 * 9 + 6 * 4;
die Multiplikation zuerst, von links nach rechts, ausgeführt. Es ergeben sich die beiden Terme 8*9 = 72 und 6*4 = 24. Damit wird der Ausdruck zu
x = 5 + 3 + 72 + 24;
Nun kommt noch die Addition von links nach rechts 5 + 3 = 8, 8 + 72 = 80, 80 + 24 = 104.
Die Rangfolge ist unbedingt zu beachten. Bestimmte Operatoren wie der Zuweisungsoperator werden von rechts nach links ausgeführt. Was passiert nun, wenn die Rangfolge nicht Ihren Vorstellungen entspricht? Sehen Sie sich dazu folgenden Ausdruck an:
SekundenGsamt = MinutenNachdenken + MinutenTippen * 60
In diesem Ausdruck soll nicht MinutenTippen
zuerst mit 60 multipliziert und dann zu
MinutenNachdenken
addiert werden. Beabsichtigt ist, zuerst die Addition der beiden Variablen
durchzuführen, um die Summe der Minuten zu ermitteln und anschließend diese
Zahl mit 60 zu multiplizieren, um die Anzahl der Sekunden zu berechnen.
In diesem Fall setzt man Klammern, um die Rangfolge zu ändern. Elemente in Klammern werden mit einer höheren Priorität ausgeführt als irgendein mathematischer Operator. Das gewünschte Ergebnis erhält man also mit
SekundenGesamt = (MinutenNachdenken + MinutenTippen) * 60
Bei komplexen Ausdrücken sind eventuell Klammern zu verschachteln. Beispielsweise ist die Anzahl der Sekunden zu berechnen und danach die Anzahl der Mitarbeiter, bevor die Multiplikation der Mitarbeiter mit den Sekunden erfolgt (um etwa die gesamte Arbeitszeit in Sekunden zu erhalten):
PersonenSekundenGesamt = ( ( (MinutenNachdenken + MinutenTippen) * 60)
* (Mitarbeiter + Beurlaubt) )
Dieser zusammengesetzte Ausdruck ist von innen nach außen zu lesen. Zuerst erfolgt
die Addition von MinutenNachdenken
und MinutenTippen
, da dieser Ausdruck in den innersten
Klammern steht. Anschließend wird diese Summe mit 60 multipliziert. Es
schließt sich die Addition von Mitarbeiter
und Beurlaubt
an. Schließlich wird die berechnete
Mitarbeiterzahl mit der Gesamtzahl der Sekunden multipliziert.
Dieses Beispiel weist auf einen wichtigen Punkt hin. Für einen Computer ist ein solcher Ausdruck leicht zu interpretieren, für einen Menschen ist er dagegen nur schwer zu lesen, zu verstehen oder zu modifizieren. Der gleiche Ausdruck in einer anderen Form mit Variablen zur Zwischenspeicherung sieht folgendermaßen aus:
MinutenGesamt = MinutenNachdenken + MinutenTippen;
SekundenGesamt = MinutenGesamt * 60;
MitarbeiterGesamt = Mitarbeiter + Beurlaubt;
PersonenSekundenGesamt = MitarbeiterGesamt * SekundenGesamt;
Dieses Beispiel verwendet zwar temporäre Variablen und erfordert mehr Schreibarbeit
als das vorherige Beispiel, ist aber leichter zu verstehen. Fügen Sie am Beginn einen
Kommentar ein, um die Absichten hinter diesem Codeabschnitt zu erläutern, und ändern
Sie die 60
in eine symbolische Konstante. Damit erhalten Sie einen Code, der
leicht zu verstehen und zu warten ist.
In früheren C++-Versionen wurden Wahrheitswerte durch Integer dargestellt. Der
neue ANSI-Standard hat jetzt einen neuen Typ eingeführt: bool
. Dieser neue Typ hat
zwei mögliche Werte false
(unwahr) oder true
(wahr).
Jeder Ausdruck kann auf seinen Wahrheitsgehalt ausgewertet werden. Ausdrücke, die
mathematisch gesehen eine Null ergeben, liefern false
zurück, alle anderen liefern
true
zurück.
Viele Compiler haben schon früher einen
bool
-Typ angeboten, der intern als einlong int
dargestellt wurde und somit vier Byte besetzt hat. Inzwischen bieten die meisten ANSI-kompatiblen Compiler einenbool
-Typ von einem Byte.
Mit den Vergleichsoperatoren (oder relationalen Operatoren) ermittelt man, ob zwei
Zahlen gleich sind, oder ob eine größer oder kleiner als die andere ist. Jeder Vergleichsausdruck
wird entweder zu true
oder zu false
ausgewertet. In Tabelle 4.1 sind
die Vergleichsoperatoren zusammengefaßt.
Der neue ANSI-Standard hat den neuen
bool
-Typ eingeführt, und alle Vergleichsoperatoren liefern jetzt einen Wert vom Typbool
zurück, alsotrue
oderfalse
. In früheren Versionen von C++ war der Rückgabewert dieser Operatoren entweder 0 fürfalse
oder ein Wert ungleich Null (normalerweise 1) fürtrue
.
Hat die Integer-Variable meinAlter
den Wert 39 und die Integer-Variable deinAlter
den Wert 40, kann man mit dem relationalen Operator »Gleich« prüfen, ob beide
gleich sind:
meinAlter == deinAlter; // ist der Wert in meinAlter der gleiche wie
// in deinAlter?
Dieser Ausdruck ergibt 0 oder false
, da die Variablen nicht gleich sind. Der Ausdruck
meinAlter > deinAlter; // ist meinAlter größer als deinAlter?
Viele Neueinsteiger in die C++-Programmierung verwechseln den Zuweisungsoperator (
=
) mit dem Gleichheitsoperator (==
). Das kann zu gemeinen Fehlern im Programm führen.
Zu den Vergleichsoperatoren gehören: Gleich (==
), Kleiner als (<
), Größer als (>
), Kleiner
oder Gleich (<=
), Größer oder Gleich (>=
) und Ungleich (!=
). Tabelle 4.1 zeigt die
Vergleichsoperatoren mit Codebeispiel und Rückgabewert.
Normalerweise verläuft der Programmfluß zeilenweise in der Reihenfolge, in der die
Anweisungen in Ihrem Quellcode stehen. Mit der if
-Anweisung kann man auf eine
Bedingung testen (beispielsweise ob zwei Variablen gleich sind) und in Abhängigkeit
vom Testergebnis zu unterschiedlichen Teilen des Codes verzweigen.
Die einfachste Form der if
-Anweisung sieht folgendermaßen aus:
if (Ausdruck)
Anweisung;
Der Ausdruck in den Klammern kann jeder beliebige Ausdruck sein, er enthält aber in
der Regel einen der Vergleichsausdrücke. Wenn der Ausdruck den Wert false
liefert,
wird die Anweisung übersprungen. Ergibt sich der Wert true
, wird die Anweisung ausgeführt.
Sehen Sie sich dazu folgendes Beispiel an:
if (grosseZahl > kleineZahl)
grosseZahl = kleineZahl;
Dieser Code vergleicht grosseZahl
mit kleineZahl
. Wenn grosseZahl
größer ist, setzt
die zweite Zeile den Wert von grosseZahl
auf den Wert von kleineZahl
.
Da ein von Klammern eingeschlossener Anweisungsblock einer einzigen Anweisung entspricht, kann die folgende Verzweigung ziemlich umfangreich und mächtig sein:
if (Ausdruck)
{
Anweisung1;
Anweisung2;
Anweisung3;
}
Zur Veranschaulichung ein einfaches Beispiel:
if (grosseZahl > kleineZahl)
{
grosseZahl = kleineZahl;
cout << "grosseZahl: " << grosseZahl << "\n ";
cout << "kleineZahl: " << kleineZahl << "\n ";
}
Diesmal wird für den Fall, daß grosseZahl
größer ist als kleineZahl
, nicht nur grosseZahl
auf den Wert von kleineZahl
gesetzt, sondern es wird auch eine Nachricht ausgegeben.
In Listing 4.5 finden Sie ein ausführlicheres Beispiel zu der Verzweigung auf
der Grundlage von Vergleichsoperatoren.
Listing 4.5: Eine Verzweigung auf der Grundlage von Vergleichsoperatoren
1: // Listing 4.5 - zeigt if-Anweisung in Verbindung
2: // mit Vergleichsoperatoren
3: #include <iostream.h>
4: int main()
5: {
6: int RedSoxScore, YankeesScore;
7: cout << "Geben Sie den Punktestand der Red Sox ein: ";
8: cin >> RedSoxScore;
9:
10: cout << "\nGeben Sie den Punktestand der Yankees ein: ";
11: cin >> YankeesScore;
12:
13: cout << "\n";
14:
15: if (RedSoxScore > YankeesScore)
16: cout << "Vorwärts Sox!\n";
17:
18: if (RedSoxScore < YankeesScore)
19: {
20: cout << "Vorwärts Yankees!\n";
21: cout << "Tolle Tage in New York!\n";
22: }
23:
24: if (RedSoxScore == YankeesScore)
25: {
26: cout << "Ein Gleichstand? Nee, das kann nicht sein.\n";
27: cout << "Geben Sie den richtigen Punktestand der Yanks ein: ";
28: cin >> YankeesScore;
29:
30: if (RedSoxScore > YankeesScore)
31: cout << "Ich wußte es! Vorwärts Sox!";
32:
33: if (YankeesScore > RedSoxScore)
34: cout << "Ich wußte es! Vorwärts Yanks!";
35:
36: if (YankeesScore == RedSoxScore)
37: cout << "Wow, es war wirklich ein Gleichstand!";
38: }
39:
40: cout << "\nDanke für die Nachricht.\n";
41: return 0;
42: }
Geben Sie den Punktestand der Red Sox ein: 10
Geben Sie den Punktestand der Yankees ein: 10
Ein Gleichstand? Nee, das kann nicht sein.
Geben Sie den richtigen Punktestand der Yanks ein: 8
Ich wußte es! Vorwärts Sox!
Danke für die Nachricht.
Dieses Programm fordert den Anwender auf, den Punktestand für zwei Baseballteams
einzugeben. Die Punkte werden in Integer-Variablen abgelegt. Die Zeilen 15, 18 und
24 vergleichen dann diese Variablen mit Hilfe einer if
-Anweisung.
Ist eine Punktzahl höher als die andere, wird eine Nachricht ausgegeben. Liegt ein Punktegleichstand vor, wird der Codeblock von Zeile 24 bis Zeile 38 ausgeführt. Die zweite Punktezahl wird erneut abgefragt und die Punkte abermals verglichen.
Ist die Punktzahl der Yankees von Anfang an höher als die der Red Sox, wird die if
-
Anweisung in Zeile 15 zu false
ausgewertet und Zeile 16 nicht aufgerufen. Der Vergleich
in Zeile 18 ist demnach true
und die Anweisungen in den Zeilen 20 und 21
werden ausgeführt. Anschließend wird die if
-Bedingung in Zeile 24 überprüft und das
Ergebnis ist folgerichtig (wenn Zeile 18 true
ergab) false
. Damit wird der ganze Codeblock
bis Zeile 39 übersprungen.
In diesem Beispiel werden trotz einer if
-Anweisung mit dem Ergebnis true
auch die
anderen if
-Anweisungen geprüft.
Viele Neueinsteiger in die C++-Programmierung machen den Fehler, ein Semikolon an die
if
-Anweisung anzuhängen:if(EinWert < 10);
EinWert = 10;Die Absicht obigen Codes ist, herauszufinden, ob
EinWert
kleiner ist als 10 und wenn ja, die Variable auf 10 zu setzen, so daß 10 der Minimalwert fürEinWert
ist. Bei Ausführung dieses Codefragments werden Sie feststellen, daßEinWert
immer auf 10 gesetzt wird! Und warum? Dieif
-Anweisung endet mit dem Semikolon (dem Tu-Nichts-Operator).Denken Sie daran, daß die Einrückung für den Compiler ohne Belang ist. Dies Fragment hätte korrekterweise auch so geschrieben werden können:
if (EinWert < 10) // pruefen
; // tue nichts
EinWert = 10; // zuweisenDurch das Entfernen des Semikolons wird die letzte Zeile Teil der
if
-Anweisung, und der Code wird genau das ausführen, was Sie wollen.
In Listing 4.5 sahen Sie eine Möglichkeit, if
-Anweisungen einzurücken. Nichts jedoch
ist besser geeignet, einen Glaubenskrieg heraufzubeschwören, als eine Gruppe von
Programmierern zu fragen, wie man am besten Anweisungen in Klammern einrückt.
Es gibt Dutzende von Möglichkeiten. Die am weitesten verbreiteten drei Möglichkeiten
möchte ich Ihnen hier vorstellen:
if
, um den Anweisungsblock zu schließen:
if (Ausdruck){
Anweisungen
}
if
ausgerichtet und die Anweisungen
werden eingerückt:
if (Ausdruck)
{
Anweisungen
}
if (Ausdruck)
{
Anweisungen
}
In diesem Buch habe ich die mittlere Alternative gewählt, da ich leichter feststellen kann, wo Anweisungsblöcke anfangen und enden, wenn die Klammern miteinander und mit der Bedingung bündig sind. Aber auch hier gilt: Es ist nicht wichtig, für welche Art der Einrückung Sie sich entscheiden, solange Sie konsequent bei der einmal gewählten Variante bleiben.
Oftmals soll ein Programm bei erfüllter Bedingung (true
) den einen Zweig durchlaufen
und bei nicht erfüllter Bedingung (false
) einen anderen. In Listing 4.5 wollten Sie eine
Nachricht (Vorwärts Sox!
) ausgeben, wenn die erste Bedingung (RedSoxScore > Yankees
) erfüllt oder true
wird, und eine andere Nachricht (Vorwärts Yanks!
), wenn der
Test false
ergibt.
Die bisher gezeigte Form, die zuerst eine Bedingung testet und dann die andere, funktioniert
zwar, ist aber etwas umständlich. Das Schlüsselwort else
trägt hier zur besseren
Lesbarkeit des Codes bei:
if (Ausdruck)
Anweisung;
else
Anweisung;
Listing 4.6 demonstriert die Verwendung des Schlüsselwortes else
.
Listing 4.6: Einsatz der else-Klausel
1: // Listing 4.6 - zeigt die if-Anweisung mit
2: // der else-Klausel
3: #include <iostream.h>
4: int main()
5: {
6: int firstNumber, secondNumber;
7: cout << "Bitte eine grosse Zahl eingeben: ";
8: cin >> firstNumber;
9: cout << "\nBitte eine kleinere Zahl eingeben: ";
10: cin >> secondNumber;
11: if (firstNumber > secondNumber)
12: cout << "\nDanke!\n";
13: else
14: cout << "\nDie zweite Zahl ist groesser!";
15:
16: return 0;
17: }
Bitte eine grosse Zahl eingeben: 10
Bitte eine kleinere Zahl eingeben: 12
Die zweite Zahl ist groesser!
Liefert die Bedingung der if
-Anweisung in Zeile 11 das Ergebnis true
, wird die Anweisung
in Zeile 12 ausgeführt. Ergibt sich der Wert false
, führt das Programm die
Anweisung in Zeile 14 aus. Wenn man die else
-Klausel in Zeile 13 entfernt, wird die
Anweisung in Zeile 14 unabhängig davon ausgeführt, ob die if
-Anweisung true
ist
oder nicht. Denken Sie daran, daß die if
-Anweisung nach Zeile 12 endet. Fehlt die
else
-Klausel, wäre Zeile 14 einfach die nächste Zeile im Programm.
Statt der einzelnen Anweisungen könnten Sie auch in geschweifte Klammern eingeschlossen Codeblöcke aufsetzen.
Die Syntax einer
if
-Anweisung sieht wie folgt aus:if (Ausdruck)
Anweisung;
nächste_Anweisung;Liefert die Auswertung von
Ausdruck
das Ergebnistrue
, wird dieAnweisung
ausgeführt und das Programm setzt mitnächste_Anweisung
fort. Ergibt der Ausdruck nichttrue
, wird dieAnweisung
ignoriert, und das Programm springt sofort zunächste_Anweisung
.
Anweisung
steht hierbei für eine einzelne, durch Semikolon abgeschlossene Anweisung oder eine in geschweifte Klammern eingeschlossene Verbundanweisung.if (Ausdruck)
Anweisung1;
else
Anweisung2;
nächste_Anweisung;Ergibt der Ausdruck
true
, wirdAnweisung1
ausgeführt, wenn nicht, kommt dieAnweisung2
zur Ausführung. Anschließend fährt das Programm mitnächste_Anweisung
fort.if (EinWert < 10)
cout << "EinWert ist kleiner als 10");
else
cout << "EinWert ist nicht kleiner als 10!");
cout << "Fertig." << endl;
In einer if
- oder else
-Klausel kann jede beliebige Anweisung stehen, sogar eine andere
if
- oder else
-Anweisung. Dadurch lassen sich komplexe if
-Anweisungen der folgenden
Form erstellen:
if (Ausdruck1)
{
if (Ausdruck2)
Anweisung1;
else
{
if (Ausdruck3)
Anweisung2;
else
Anweisung3;
}
}
else
Anweisung4;
Diese umständliche if
-Anweisung sagt aus: »Wenn Ausdruck1
gleich true
ist und
Ausdruck2
gleich true
ist, führe Anweisung1
aus. Wenn Ausdruck1
gleich true
, aber
Ausdruck2
nicht true
ist, dann führe Anweisung2
aus, wenn Ausdruck3
true
ist. Wenn
Ausdruck1
gleich true
, aber Ausdruck2
und Ausdruck3
gleich false
sind, führe
Anweisung3
aus. Wenn schließlich Ausdruck1
nicht true
ist, führe Anweisung4
aus.« Wie
man sieht, können komplexe if
-Anweisungen einiges zur Verwirrung beitragen!
Listing 4.7 zeigt ein Beispiel einer derartigen komplexen if
-Anweisung.
Listing 4.7: Verschachtelte if-Anweisung
1: // Listing 4.7 - Verschachtelte
2: // if-Anweisung
3: #include <iostream.h>
4: int main()
5: {
6: // Zwei Zahlen abfragen
7: // Die Zahlen an firstNumber und secondNumber zuweisen
8: // Wenn firstNumber groesser als secondNumber ist,
9: // testen, ob sie ohne Rest teilbar sind
10: // Wenn ja, testen, ob die Zahlen gleich sind
11:
12: int firstNumber, secondNumber;
13: cout << "Bitte zwei Zahlen eingeben.\nDie erste: ";
14: cin >> firstNumber;
15: cout << "\nDie zweite: ";
16: cin >> secondNumber;
17: cout << "\n\n";
18:
19: if (firstNumber >= secondNumber)
20: {
21: if ( (firstNumber % secondNumber) == 0) // ganzzahlig teilbar?
22: {
23: if (firstNumber == secondNumber)
24: cout << "Beide Zahlen sind gleich!\n";
25: else
26: cout << "Zahlen ohne Rest teilbar!\n";
27: }
28: else
29: cout << "Zahlen nicht ohne Rest teilbar!\n";
30: }
31: else
32: cout << "Die zweite Zahl ist groesser!\n";
33: return 0;
34: }
Bitte zwei Zahlen eingeben.
Die erste: 10
Die zweite: 2
Zahlen ohne Rest teilbar!
Das Programm fordert zur Eingabe von zwei Zahlen auf und vergleicht sie dann. Die
erste if
-Anweisung in Zeile 19 prüft, ob die erste Zahl größer oder gleich der zweiten
ist. Falls nicht, kommt die else
-Klausel in Zeile 31 zur Ausführung.
Wenn die erste if
-Bedingung gleich true
ist, wird der in Zeile 20 beginnende Codeblock
ausgeführt und die zweite if
-Anweisung in Zeile 21 getestet. Hier erfolgt die
Prüfung, ob die erste Zahl modulo der zweiten keinen Rest liefert. Wenn das so ist,
sind die Zahlen entweder ohne Rest teilbar oder einander gleich. Die if
-Anweisung in
Zeile 23 prüft auf Gleichheit und zeigt die entsprechende Meldung an.
Wenn die if
-Anweisung in Zeile 21 false
ergibt, wird die else
-Anweisung in Zeile 28
ausgeführt.
Obwohl es zulässig ist, die geschweiften Klammern in if
-Anweisungen mit nur einer
einzelnen Anweisung wegzulassen, und es ebenfalls zulässig ist, if
-Anweisungen wie
if (x > y) // Wenn x groesser als y ist,
if (x < z) // und wenn x kleiner als z ist,
x = y; // dann setze x auf den Wert in z.
zu verschachteln, kann das bei umfangreich verschachtelten Anweisungen kaum noch
zu durchschauen sein. Denken Sie daran, daß man mit Whitespace-Zeichen und Einzügen
die Lesbarkeit des Quelltextes erhöhen kann und der Compiler von diesen gestalterischen
Elementen keine Notiz nimmt. Man kann leicht die Logik durcheinanderbringen
und versehentlich eine else
-Anweisung der falschen if
-Anweisung zuordnen.
Listing 4.8 verdeutlicht dieses Problem.
Listing 4.8: Klammern in if- und else-Klauseln
1: // Listing 4.8 - demonstriert, warum Klammern in verschachtelten
2: // if-Anweisungen so wichtig sind
3: #include <iostream.h>
4: int main()
5: {
6: int x;
7: cout << "Eine Zahl kleiner als 10 oder groesser als 100 eingeben: ";
8: cin >> x;
9: cout << "\n";
10:
11: if (x > 10)
12: if (x > 100)
13: cout << "Groesser als 100. Danke!\n";
14: else // nicht das beabsichtigte else!
15: cout << "Kleiner als 10. Danke!\n";
16:
17: return 0;
18: }
Eine Zahl kleiner als 10 oder groesser als 100 eingeben: 20
Kleiner als 10. Danke!
Der Programmierer hatte die Absicht, nach einer Zahl zwischen 10 und 100 zu fragen, auf den korrekten Wert zu testen und sich dann zu bedanken.
Wenn die if
-Anweisung in Zeile 11 den Wert true
liefert, wird die folgende Anweisung
(Zeile 12) ausgeführt. In diesem Beispiel erfolgt die Ausführung von Zeile 12,
wenn die eingegebene Zahl größer als 10 ist. Zeile 12 enthält ebenfalls eine if
-Anweisung.
Diese if
-Anweisung liefert true
, wenn die eingegebene Zahl größer als 100 ist.
Wenn die Zahl nicht größer als 100 ist, wird die Anweisung in Zeile 15 ausgeführt.
Wenn die eingegebene Zahl kleiner als 10 ist, liefert die if
-Anweisung in Zeile 11 den
Wert false
. Die Programmsteuerung geht an die auf if
folgende Zeile, in diesem Fall
Zeile 17, über. Wenn man eine Zahl kleiner als 10 eingibt, sieht die Ausgabe wie folgt
aus:
Eine Zahl kleiner als 10 oder groesser als 100 eingeben: 9
Die else
-Klausel in Zeile 14 sollte offensichtlich zur if
-Anweisung von Zeile 11 gehören,
was auch der Einzug im Quelltext dokumentiert. Leider ist die else
-Anweisung
tatsächlich mit if
aus Zeile 12 verbunden. Damit enthält dieses Programm einen nicht
auf Anhieb erkennbaren Fehler, der vom Compiler nicht bemängelt wird.
Wir haben es hier mit einem zulässigen C++-Programm zu tun, das aber nicht wie vorgesehen arbeitet. Dazu kommt noch, daß bei den meisten Tests, die der Programmierer durchführt, das Programm zu laufen scheint. Solange man eine Zahl größer als 100 eingibt, funktioniert alles wunderbar.
In Listing 4.9 ist das Problem durch Einfügen der erforderlichen Klammern beseitigt.
Listing 4.9: Richtige Verwendung von geschweiften Klammern bei einer if-Anweisung
1: // Listing 4.9 - Demonstriert die richtige Verwendung
2: // von Klammern in verschachtelten if-Anweisungen
3: #include <iostream.h>
4: int main()
5: {
6: int x;
7: cout << "Eine Zahl kleiner als 10 oder groesser als 100 eingeben: ";
8: cin >> x;
9: cout << "\n";
10:
11: if (x > 10)
12: {
13: if (x > 100)
14: cout << "Groesser als 100. Danke!\n";
15: }
16: else
17: cout << "Kleiner als 10. Danke!\n";
18: return 0;
19: }
Eine Zahl kleiner als 10 oder groesser als 100 eingeben: 20
Die geschweiften Klammern in den Zeilen 12 und 15 schließen alle dazwischenliegenden
Anweisungen zu einem Block zusammen. Nunmehr gehört die else
-Klausel in
Zeile 16 wie beabsichtigt zur if
-Anweisung in Zeile 11.
Der Anwender hat die Zahl 20 eingegeben, so daß die if
-Anweisung in Zeile 11 den
Wert true
liefert. Die if
-Anweisung in Zeile 13 liefert false
, so daß keine Ausgabe erfolgt.
Besser wäre es, wenn der Programmierer eine weitere else
-Klausel nach Zeile
14 vorgesehen hätte, um fehlerhafte Eingaben abzufangen und eine entsprechende
Meldung anzuzeigen.
Die im Buch gezeigten Programme sind bewußt einfach gehalten und dienen nur dazu, das jeweils behandelte Thema zu verdeutlichen. Es wurde auf »narrensicheren« Code verzichtet, der einen Schutz gegen fehlerhafte Eingaben bietet. In professionellen Programmen muß man mit jedem möglichen Benutzerfehler rechnen und diese Fehler entsprechend »sanft« abfangen.
Oftmals muß man mehrere Vergleiche auf einmal anstellen. »Ist es richtig, daß x
größer
ist als y
, und ist es ebenso richtig, daß y
größer ist als z
?« Für ein Programm könnte
es wichtig sein, zu ermitteln, ob beide Bedingungen true
sind oder ob irgendeine
andere Bedingung true
ist, bevor es eine bestimmte Aktion ausführt.
Stellen Sie sich ein intelligentes Alarmsystem vor, das nach folgender Logik arbeitet: »Wenn der Türalarm ausgelöst wird UND es später als 18:00 Uhr ist UND es sich NICHT um die Urlaubszeit handelt ODER gerade Wochenende ist, dann rufe die Polizei.« Eine derartige Auswertung läßt sich mit den drei logischen Operatoren von C++ realisieren. Tabelle 4.2 zeigt diese Operatoren.
Eine logische AND
-Anweisung wertet zwei Ausdrücke aus. Sind beide Ausdrücke true
,
liefert die logische AND
-Anweisung ebenfalls true
. Wenn es wahr (true
) ist, daß Sie
hungrig sind, UND (AND) es wahr ist, daß Sie Geld haben, dann ist es wahr, daß Sie
ein Mittagessen kaufen können. In diesem Sinn wird
if ( (x == 5) && (y == 5) )
zu true
ausgewertet, wenn sowohl x
als auch y
gleich 5 ist. Der Ausdruck liefert false
,
wenn mindestens eine der beiden Variablen nicht gleich 5 ist. Beachten Sie, daß beide
Seiten true
sein müssen, damit der gesamte Ausdruck zu true
wird.
Beachten Sie weiterhin, daß das logische AND
aus zwei kaufmännischen Und-Zeichen
(&&
) besteht.
Eine logische OR
-Anweisung wertet zwei Ausdrücke aus. Wenn einer der beiden true
ist, ergibt sich der Ausdruck zu true
. Wenn man Geld ODER eine Kreditkarte hat,
kann man die Rechnung bezahlen. Beide Zahlungsmittel sind nicht gleichzeitig erforderlich,
man braucht nur eins, auch wenn es nichts schadet, beides zur Verfügung zu
haben.
if ( (x == 5) || (y == 5) )
liefert true
, wenn x
oder y
gleich 5 ist oder wenn beide gleich 5 sind.
Beachten Sie, daß das logische OR
aus zwei senkrechten Strichen (||
) besteht.
Eine logische NOT
-Anweisung liefert true
, wenn der zu testende Ausdruck false
ergibt.
Der Ausdruck
if ( !( == 5) )
ist nur dann true
, wenn x
nicht gleich 5 ist. Den gleichen Test kann man auch als
if (x != 5)
Wenn der Compiler eine AND
-Anweisung wie
if ( (x == 5) && (y == 5) )
prüft, wird zuerst die erste Bedingung (x == 5) getestet. Ergibt sich bereits hier der
Wert false
, das heißt, x ist nicht gleich 5, wird der Compiler erst gar NICHT mit der
Prüfung der zweiten Anweisung (y == 5) fortfahren, da eine AND
-Anweisung zur Bedingung
hat, daß beide Anweisungen erfüllt werden.
Ähnlich verläuft es auch bei OR
-Anweisungen wie
if ( (x == 5) || (y == 5) )
Ergibt die erste Anweisung (x == 5) den Wert true
, nimmt der Compiler die weitere
Auswertung der zweiten Anweisung (y == 5) nicht mehr vor, da es bei einer OR
-Anweisung
reicht, wenn eine Bedingung erfüllt ist.
Vergleichsoperatoren und logische Operatoren, die man in C++-Ausdrücken einsetzt,
liefern einen Wert zurück: true
oder false
. Wie bei allen Ausdrücken gibt es auch hier
eine Rangfolge, die bestimmt, welcher Vergleich zuerst ausgewertet wird (siehe auch
Anhang A). Diese Tatsache ist wichtig, wenn man den Wert einer Anweisung wie
if ( x > 5 && y > 5 || z > 5)
bestimmt. Der Programmierer könnte beabsichtigen, daß dieser Ausdruck nur zu true
ausgewertet wird, wenn sowohl x
als auch y
größer als 5 oder wenn z
größer als 5 ist.
Andererseits kann der Programmierer auch wollen, daß dieser Ausdruck nur dann zu
true
wird, wenn x
größer als 5 ist und wenn es auch wahr ist, daß entweder y
oder z
größer als 5 ist.
Wenn x
gleich 3 ist und sowohl y
als auch z
gleich 10 ist, liefert die erste Interpretation
das Ergebnis true
(z
ist größer als 5, so daß x
und y
ignoriert werden), während die
zweite Auslegung false
ergibt (es ist nicht wahr, daß x
größer als 5 ist, deshalb ist es
auch unerheblich, was rechts des &&
-Symbols steht, da beide Seiten zutreffen müssen).
Die von der Rangfolge bestimmte Reihenfolge der Vergleiche kann man mit Klammern sowohl ändern als auch deutlicher darstellen:
if ( (x > 5) && (y > 5 || z > 5) )
Mit den obigen Werten liefert diese Anweisung das Ergebnis false
. Da es nicht wahr
ist, daß x
größer als 5 ist, ergibt die linke Seite der AND
-Anweisung false
, und damit
wird die gesamte Anweisung zu false
. Denken Sie daran, daß bei der AND
-Anweisung
beide Seiten true
sein müssen - etwas kann nicht »gutschmeckend« UND »gut für
Dich« sein, wenn es nicht gut schmeckt.
Es empfiehlt sich, mit zusätzlichen Klammern klarzustellen, was man gruppieren möchte. Das Ziel sollte immer ein funktionsfähiges Programm sein, das auch leicht zu lesen und zu verstehen sein soll.
C++ behandelt 0 als false
und jeden anderen Wert als true
. Da Ausdrücke immer einen
Wert haben, nutzen viele C++-Programmierer diese Tatsache in ihren if
-Anweisungen
aus. Eine Anweisung wie
if (x) // wenn x gleich TRUE (ungleich Null)
x = 0;
kann man lesen als »wenn x
einen Wert ungleich 0 hat, setze x
auf 0«. Hier besteht
»Verdunklungsgefahr«! Klarer formuliert man
if (x != 0) // wenn x ungleich 0
x = 0;
Beide Anweisungen sind zulässig, wobei aber die zweite verständlicher ist. Es gehört zum guten Programmierstil, die erste Methode für echte logische Tests zu reservieren, statt auf Werte ungleich 0 zu testen.
Die folgenden Anweisungen sind ebenfalls äquivalent:
if (!x) // wenn x gleich false ist (Null)
if (x == 0) // wenn x gleich Null ist
Die zweite Anweisung ist allerdings etwas einfacher zu verstehen und aussagekräftiger, wenn es darum geht, den mathematischen Wert von x und nicht dessen logischen Status zu ermitteln.
Der Bedingungsoperator (?:
) ist der einzige ternäre Operator in C++, das heißt, es ist
der einzige Operator, der drei Operanden übernimmt.
Der Bedingungsoperator übernimmt drei Ausdrücke und liefert einen Wert zurück:
(Ausdruck1) ? (Ausdruck2) : (Ausdruck3)
Diese Zeile läßt sich auch wie folgt ausdrücken: »Wenn Ausdruck1
true
ist, liefere den
Wert von Ausdruck2
zurück; andernfalls den Wert von Ausdruck3
«. In der Regel wird
dieser Wert einer Variablen zugewiesen.
In Listing 4.10 finden Sie eine if
-Anweisung und einen äquivalenten Einsatz des Bedingungsoperators.
Listing 4.10: Der Bedingungsoperator
1: // Listing 4.10 - zeigt den Bedingungsoperator
2: //
3: #include <iostream.h>
4: int main()
5: {
6: int x, y, z;
7: cout << "Geben Sie zwei Zahlen ein.\n";
8: cout << "Erstens: ";
9: cin >> x;
10: cout << "\nZweitens: ";
11: cin >> y;
12: cout << "\n";
13:
14: if (x > y)
15: z = x;
16: else
17: z = y;
18:
19: cout << "z: " << z;
20: cout << "\n";
21:
22: z = (x > y) ? x : y;
23:
24: cout << "z: " << z;
25: cout << "\n";
26: return 0;
27: }
Geben Sie zwei Zahlen ein.
Erstens: 5
Zweitens: 8
z: 8
z: 8
Es werden drei Integer-Variablen erzeugt: x
, y
und z
. Den ersten zwei werden durch
die Benutzereingaben Werte zugewiesen. Die if
-Anweisung in Zeile 14 prüft, welche
davon die größere ist und weist die größere Zahl der Variablen z zu.
Der Bedingungsoperator in Zeile 22 führt den gleichen Test durch und weist ebenfalls
z
den größeren Wert zu. Gelesen wird die Anweisung wie folgt: »Ist x
größer als y,
liefere
den Wert von x
zurück, andernfalls den Wert von y
«. Der zurückgelieferte Wert
wird z
zugewiesen. Zeile 24 gibt diesen Wert aus. Damit wird deutlich, daß die Bedingungs-Anweisung
eine Kurzform für die if...else
-Anweisung ist.
Der Stoff dieses Kapitels war ziemlich umfangreich. Sie haben gelernt, was in C++
Anweisungen und Ausdrücke sind, wozu Operatoren dienen und wie if
-Anweisungen
arbeiten.
Es wurde gezeigt, daß ein Anweisungsblock in Klammern überall dort stehen kann, wo auch eine einfache Anweisung steht.
Weiterhin haben Sie gelernt, daß jeder Ausdruck einen Wert zurückliefert und daß dieser
Wert mit einer if
-Anweisung oder einem Bedingungsoperator überprüft werden
kann. Sie wissen jetzt, wie man mehrere Anweisungen mit logischen Operatoren auswertet,
wie man Werte mit dem Vergleichsoperatoren vergleicht und wie man Werte
mit dem Zuweisungsoperator zuweist.
Außerdem wurde Ihnen die Rangfolge der Operatoren vorgestellt und gezeigt, wie man mit Klammern diese Rangfolge ändern und deutlicher hervorheben kann, so daß es leichter wird, den Code zu warten.
Frage:
Warum verwendet man unnötige Klammern, wenn der Vorrang bestimmt,
welche Operatoren zuerst auszuwerten sind?
Antwort:
Obwohl es stimmt, daß der Compiler den Vorrang kennt und daß ein Programmierer
bei Bedarf die Vorrangregeln nachschlagen kann, ist der geklammerte
Code leichter zu verstehen und leichter zu warten.
Frage:
Wenn die Vergleichsoperatoren immer true
oder false
zurückgeben,
warum werden dann alle Werte ungleich Null als true
angesehen?
Antwort:
Die Vergleichsoperatoren geben true
oder false
zurück, aber jeder Ausdruck
liefert einen Wert, und diese Werte lassen sich ebenfalls in einer if
-Anweisung
auswerten. Dazu ein Beispiel:
if ( (x = a + b) == 35 )
Das ist eine durchaus zulässige C++-Anweisung, die auch dann einen Wert liefert, wenn die Summe von
a
undb
nicht gleich 35 ist. Beachten Sie auch, daßx
in jedem Fall den Wert erhält, der sich aus der Addition vona
undb
ergibt.
Frage:
Welche Wirkung haben Tabulatoren, Leerzeichen und Zeilenschaltungen
im Programm?
Antwort:
Tabulatoren, Leerzeichen und Zeilenschaltungen (sogenannte Whitespace-Zeichen)
haben auf das Programm keinen Einfluß. Allerdings läßt sich mit diesen
Zeichen der Quelltext gestalten und damit die Lesbarkeit verbessern.
Frage:
Sind negative Zahlen true
oder false
?
Antwort:
Alle Zahlen ungleich 0 - sowohl positive als auch negative - sind true
.
Der Workshop enthält Quizfragen, die Ihnen helfen sollen, Ihr Wissen zu festigen, und Übungen, die Sie anregen sollen, das eben Gelernte umzusetzen und eigene Erfahrungen zu sammeln. Versuchen Sie, das Quiz und die Übungen zu beantworten und zu verstehen, bevor Sie die Lösungen in Anhang D lesen und zur Lektion des nächsten Tages übergehen.
meinAlter
, a
und b
Integer-Variablen sind, wie lauten Ihre Werte nach
meinAlter = 39;
a = meinAlter++;
b = ++meinAlter;
if(x = 3)
und if(x == 3)
?
true
oder false
?
0
1
-1
x = 0
x == 0 // angenommen x hat den Wert 0
if
-Anweisung, die zwei Integer-Variablen überprüft und die größere in die kleinere umwandelt. Verwenden Sie nur eine else
-Klausel.
1: #include <iostream.h>
2: int main()
3: {
4: int a, b, c;
5: cout << "Bitte drei Zahlen eingeben:\n";
6: cout << "a: ";
7: cin >> a;
8: cout << "\nb: ";
9: cin >> b;
10: cout << "\nc: ";
11: cin >> c;
12:
13: if (c = (a-b))
14: {cout << "a: ";
15: cout << a;
16: cout << "minus b: ";
17: cout << b;
18: cout << "gleich c: ";
19: cout << c << endl;}
20: else
21: cout << "a-b ist nicht gleich c: " << endl;
22: return 0;
23: }
1: #include <iostream.h>
2: int main()
3: {
4: int a = 1, b = 1, c;
5: if (c = (a-b))
6: cout << "Der Wert von c ist: " << c;
7: return 0;
8: }
© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH