vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


Woche 1

Tag 4


Ausdrücke und Anweisungen

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.

Heute lernen Sie,

Anweisungen

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.

Whitespace

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;

oder

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).

Blöcke und Verbundanweisungen

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.

Was Sie tun sollten

Schreiben Sie immer eine schließende geschweifte Klammer, wenn eine öffnende geschweifte Klammer vorhanden ist.

Schließen Sie Anweisungen mit einem Semikolon ab.

Setzen Sie Whitespace-Zeichen sinnvoll ein, um den Code deutlicher zu präsentieren.

Ausdrücke

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.

Der Ausdruck

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.

Operatoren

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:

Zuweisungsoperator

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!

nicht erlaubt ist.

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).

Mathematische Operatoren

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.

Integer-Division und Modulo

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 durch 3 teile, erhalte ich 1. 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 eben 1.

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.

Zusammengesetzte Operatoren

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 (%=).

Inkrementieren und Dekrementieren

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;

Präfix und Postfix

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.

Schreibt man anschließend

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.

Rangfolge der Operatoren

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

Verschachtelte Klammern

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.

Was Sie tun sollten

... und was nicht

Denken Sie daran, daß Ausdrucke einen Wert haben.

Verwenden Sie den Präfix-Operator (++Variable), um die Variable zu inkrementieren oder zu dekrementieren, bevor sie in einem Ausdruck verwendet wird.

Verwenden Sie den Postfix-Operator (Variable++), um die Variable zu inkrementieren oder zu dekrementieren, nachdem sie verwendet wurde.

Verwenden Sie Klammern, um die Rangfolge bei der Abarbeitung der Operatoren zu ändern.

Verschachteln Sie Ihre Ausdrücke nicht zu sehr, da sie sonst schwer verständlich werden und die Wartung erschwert wird.

Wahrheitswerte

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 ein long int dargestellt wurde und somit vier Byte besetzt hat. Inzwischen bieten die meisten ANSI-kompatiblen Compiler einen bool-Typ von einem Byte.

Vergleichsoperatoren

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 Typ bool zurück, also true oder false. In früheren Versionen von C++ war der Rückgabewert dieser Operatoren entweder 0 für false oder ein Wert ungleich Null (normalerweise 1) für true.

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?

liefert 0 oder false.

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.

Operator

Symbol

Beispiel

Rückgabewert

Gleich

==

100 == 50;

FALSE

50 == 50;

TRUE

Ungleich

!=

100 != 50;

TRUE

50 != 50

FALSE

Größer als

>

100 > 50;

TRUE

50 > 50;

FALSE

Größer als oder Gleich

>=

100 >= 50;

TRUE

50 >= 50

TRUE

Kleiner als

<

100 < 50;

FALSE

50 < 50;

FALSE

Kleiner als oder Gleich

<=

100 <= 50;

FALSE

50 <= 50;

TRUE

Tabelle 4.1: Die Vergleichsoperatoren

Was Sie tun sollten

... und was nicht

Denken Sie daran, daß Vergleichsoperatoren entweder den Wert true oder false zurückliefern.

Verwechseln Sie den Zuweisungsoperator (=) nicht mit dem Gleichheitsoperator (==). Dies ist einer der häufigsten Fehler in der C++-Programmierung. Also seien Sie vorsichtig!

Die if-Anweisung

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ür EinWert ist. Bei Ausführung dieses Codefragments werden Sie feststellen, daß EinWert immer auf 10 gesetzt wird! Und warum? Die if-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; // zuweisen

Durch das Entfernen des Semikolons wird die letzte Zeile Teil der if-Anweisung, und der Code wird genau das ausführen, was Sie wollen.

Einrückungsarten

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:

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.

Die else-Klausel

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 if-Anweisung

Die Syntax einer if-Anweisung sieht wie folgt aus:

Format 1:

if (Ausdruck)
Anweisung;
nächste_Anweisung;

Liefert die Auswertung von Ausdruck das Ergebnis true, wird die Anweisung ausgeführt und das Programm setzt mit nächste_Anweisung fort. Ergibt der Ausdruck nicht true, wird die Anweisung ignoriert, und das Programm springt sofort zu nä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, wird Anweisung1 ausgeführt, wenn nicht, kommt die Anweisung2 zur Ausführung. Anschließend fährt das Programm mit nächste_Anweisung fort.

Beispiel 1

if (EinWert < 10)
cout << "EinWert ist kleiner als 10");
else
cout << "EinWert ist nicht kleiner als 10!");
cout << "Fertig." << endl;

Erweiterte if-Anweisungen

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.

Geschweifte Klammern in verschachtelten if-Anweisungen

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.

Logische Operatoren

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.

Operator

Symbol

Beispiel

AND

&&

Ausdruck1 && Ausdruck2

OR

||

Ausdruck1 || Ausdruck2

NOT

!

!Ausdruck

Tabelle 4.2: Logische Operatoren

Logisches AND

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.

Logisches OR

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.

Der Ausdruck

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.

Logisches NOT

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)

schreiben.

Verkürzte Prüfung

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.

Rangfolge der Vergleichsoperatoren

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.

Mehr über Wahr und Unwahr

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.

Was Sie tun sollten

... und was nicht

Verwenden Sie für Ihre logischen Tests Klammern, um Ihre Absicht und die Rangfolge deutlicher zu machen.

Verwenden Sie geschweifte Klammern in verschachtelten if-Anweisungen, um die else-Anweisungen hervorzuheben und Fehler zu vermeiden.

Verwenden Sie if (x) nicht als Synonym für if (x != 0), denn letzteres ist klarer.

Verwenden Sie if (!x) nicht als Synonym für if (x == 0), denn letzteres ist klarer.

Der Bedingungsoperator

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.

Zusammenfassung

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.

Fragen und Antworten

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 )

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.

Workshop

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.

Quiz

  1. Was ist ein Ausdruck?
  2. Ist x = 5 + 7 ein Ausdruck? Was ist sein Wert?
  3. Was ist der Wert von 201 / 4?
  4. Was ist der Wert von 201 % 4?
  5. Wenn meinAlter, a und b Integer-Variablen sind, wie lauten Ihre Werte nach
    meinAlter = 39;
    a = meinAlter++;
    b = ++meinAlter;
  6. Was ist der Wert von 8 + 2 * 3?
  7. Was ist der Unterschied zwischen if(x = 3) und if(x == 3)?
  8. Ergeben die folgenden Werte true oder false?
    0
    1
    -1
    x = 0
    x == 0 // angenommen x hat den Wert 0

Übungen

  1. Schreiben Sie eine einzige if-Anweisung, die zwei Integer-Variablen überprüft und die größere in die kleinere umwandelt. Verwenden Sie nur eine else-Klausel.
  2. Überprüfen Sie das folgende Programm. Stellen Sie sich vor, sie geben drei Zahlen ein und notieren Sie sich, was Sie als Ausgabe erwarten.
    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: }
  3. Geben Sie das Programm aus Übung 2 ein, kompilieren und linken Sie es und starten Sie es dann. Geben Sie die Zahlen 20, 10 und 50 ein. Hat die Ausgabe Ihren Erwartungen entsprochen? Wenn nein, warum nicht?
  4. Überprüfen Sie das folgende Programm und raten Sie, wie die Ausgabe lautet.
    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: }
  5. Erfassen, kompilieren, linken und starten Sie das Programm aus Übung 4. Wie lautete die Ausgabe? Warum?


vorheriges KapitelInhaltsverzeichnisStichwortverzeichnisFeedbacknächstes Kapitel


© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH