Programme verbringen den größten Teil ihrer Arbeit mit Verzweigungen und Schleifen.
In Kapitel 4, »Ausdrücke und Anweisungen«, haben Sie gelernt, wie Sie mit der
if
-Anweisung Ihr Programm verzweigen können. Heute lernen Sie,
if...else
-Anweisungen
aufbaut.
Viele Probleme der Programmierung lassen sich durch wiederholtes Ausführen einer oder mehrerer Anweisungen lösen. Dazu bieten sich zwei Möglichkeiten an. Zum einen die Rekursion (die bereits in Kapitel 5, »Funktionen«, behandelt wurde) und zum anderen die Iteration. Iteration bedeutet die wiederholte Ausführung einer Aktion und wird üblicherweise in Form von Schleifen implementiert.
In den frühen Tagen der Informatik waren Programme primitiv, unverständlich und kurz. Schleifen bestanden aus einer Sprungmarke (Label), einigen Anweisungen und einem Sprung.
In C++ ist ein Label einfach ein Name, gefolgt von einem Doppelpunkt. Das Label
steht links von einer zulässigen C++-Anweisung. Einen Sprung realisiert man mit der
Anweisung goto
und dem sich anschließenden Namen des Labels. Listing 7.1 zeigt
dazu ein Beispiel.
Listing 7.1: Schleifenkonstruktion mit goto
1: // Listing 8.1
2: // Schleifen mit goto
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int counter = 0; // Zähler initialisieren
9: loop: counter ++; // Anfang der Schleife
10: cout << "Zaehler: " << counter << "\n";
11: if (counter < 5) // Den Wert testen
12: goto loop; // Sprung an Anfang der Schleife
13:
14: cout << "Fertig. Zaehler: " << counter << ".\n";
15: return 0;
16: }
Zähler: 1
Zähler: 2
Zähler: 3
Zähler: 4
Zähler: 5
Fertig. Zaehler: 5.
Zeile 8 initialisiert den Zähler (counter
) mit 0. Das Label loop
in Zeile 9 markiert den
Beginn der Schleife. Das Programm inkrementiert den Wert von counter
und gibt den
neuen Wert aus. Zeile 11 testet den Wert von counter
. Ist er kleiner als 5, liefert die
if
-Anweisung das Ergebnis true
. Das Programm führt daraufhin die goto
-Anweisung
aus und springt zurück zu Zeile 9. Der Schleifendurchlauf wiederholt sich, bis counter
gleich 5 ist. Das Programm übergeht dann die Schleife und führt die letzte Ausgabeanweisung
(Zeile 14) aus.
goto
-Konstruktionen sind schon früh ins Kreuzfeuer der Kritik geraten, und das nicht
einmal zu unrecht. Mit goto
-Anweisungen läßt sich ein Sprung zu einer beliebigen
Stelle im Quellcode realisieren, rückwärts oder vorwärts. Die unüberlegte Verwendung
von goto
-Anweisungen führt zu unübersichtlichen, schlechten und schwer zu lesenden
Programmen, die man als Spaghetti-Code bezeichnet. Deshalb hämmern Informatik-
Dozenten ihren Studenten seit über zwanzig Jahren ein, goto
möglichst nicht zu verwenden.
Der strukturierten Programmierung kommen die intelligenteren Schleifenbefehle for
,
while
und do...while
entgegen, mit denen man goto
-Konstruktionen von vornherein
vermeiden kann. Man kann jedoch zu Recht argumentieren, daß mal wieder alles etwas
übertrieben wird. Denn wie jedes Werkzeug kann goto
, sorgfältig eingesetzt und
in den richtigen Händen, eine nützliche Konstruktion sein, und das ANSI-Komitee hat
entschieden, goto
weiter in der Sprache zu behalten, da es seine berechtigten Einsatzbereiche
hat. Aber wie sagt man so schön »Kinder, macht das nicht zu Hause nach.«
Die Syntax der
goto
-Anweisung besteht aus demgoto
-Befehl und einem Labelnamen. Damit springen Sie, ohne weitere Bedingungen zu berücksichtigen, zu dem Label.
goto
zu verwenden ist fast immer ein Zeichen für einen schlechten Entwurf. Am besten versuchen Sie,goto
zu vermeiden. In meinen 10 Jahren als Programmierer habe ich es erst einmal verwendet.
Eine while
-Schleife bewirkt die Wiederholung einer Folge von Anweisungen im Programm,
solange die Startbedingung gleich true
bleibt. Das goto
-Beispiel in Listing 7.1
inkrementiert den Zähler (counter
), bis er den Wert 5 erreicht. Listing 7.2 zeigt das
gleiche Programm, das jetzt mit der while
-Konstruktion realisiert ist.
1: // Listing 7.2
2: // Schleifen mit while
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int counter = 0; // Bedingung initialisieren
9:
10: while(counter < 5) // Testbedingung immer noch true
11: {
12: counter++; // Rumpf der Schleife
13: cout << "Zähler: " << counter << "\n";
14: }
15:
16: cout << "Fertig. Zaehler: " << counter << ".\n";
17: return 0;
18: }
Zähler: 1
Zähler: 2
Zähler: 3
Zähler: 4
Zähler: 5
Fertig. Zaehler: 5.
Dieses einfache Beispiel demonstriert die Grundlagen der while
-Schleife. Liefert der
Test einer Bedingung den Wert true
, wird der Rumpf der while
-Schleife ausgeführt.
Im Beispiel testet die Bedingung in Zeile 10, ob counter
kleiner als 5 ist. Ergibt der
Test true
, führt das Programm den Rumpf der Schleife aus: Zeile 12 inkrementiert
den Zähler, und Zeile 13 gibt den Wert aus. Wenn die Bedingungsanweisung in Zeile
10 den Wert false
ergibt (wenn counter
nicht mehr kleiner als 5 ist), wird der gesamte
Rumpf der while
-Schleife (in den Zeilen 11 bis 14) übersprungen. Die Programmausführung
setzt dann sofort mit Zeile 15 fort.
Die Syntax der
while
-Anweisung lautet:while ( bedingung )
anweisung;
bedingung
ist ein beliebiger C++-Ausdruck undanweisung
eine beliebige C++-Anweisung oder ein Block von Anweisungen. Ergibt diebedingung
true
(1), wirdanweisung
ausgeführt und danachbedingung
erneut getestet. Dieser Vorgang wiederholt sich so lange, bis der Test vonbedingung
false
ergibt. Daraufhin wird diewhile
-Schleife verlassen, und die Ausführung setzt mit der ersten Zeile unteranweisung
fort.// bis 10 zählen
int x = 0;
while (x < 10)
cout << "X: " << x++;
Die in einer while
-Schleife getestete Bedingung kann aus jedem zulässigen C++-Ausdruck
bestehen. Es lassen sich auch Ausdrücke einbinden, die man mit den logischen
Operatoren &&
(AND), ||
(OR) und !
(NOT) erstellt. Listing 7.3 zeigt eine etwas kompliziertere
while
-Anweisung.
Listing 7.3: Komplexe while-Schleifen
1: // Listing 7.3
2: // Komplexe while-Anweisungen
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: unsigned short small;
9: unsigned long large;
10: const unsigned short MAXSMALL=65535;
11:
12: cout << "Bitte eine kleine Zahl eingeben: ";
13: cin >> small;
14: cout << "Bitte eine grosse Zahl eingeben: ";
15: cin >> large;
16:
17: cout << "Klein: " << small << "...";
18:
19: // Bei jedem Schleifendurchlauf drei Bedingungen testen
20: while (small < large && large > 0 && small < MAXSMALL)
21:
22: {
23: if (small % 5000 == 0) // Alle 5000 Zeilen einen Punkt ausgeben
24: cout << ".";
25:
26: small++;
27:
28: large-=2;
29: }
30:
31: cout << "\nKlein: " << small << " Gross: " << large << endl;
32: return 0;
33: }
Bitte eine kleine Zahl eingeben: 2
Bitte eine grosse Zahl eingeben: 100000
Klein: 2.........
Klein: 33335 Gross: 33334
Dieses Programm stellt ein Spiel dar. Man gibt zwei Zahlen ein, eine kleine (small
)
und eine große (large
). Die kleinere Zahl wird um 1 nach oben gezählt, die größere in
Schritten von 2 abwärts. Es ist nun zu erraten, wann sich die Zahlen treffen.
In den Zeilen 12 bis 15 erfolgt die Eingabe der Zahlen. Zeile 20 richtet eine while
-
Schleife ein, deren Durchläufe von drei Bedingungen abhängig sind:
small
ist nicht größer als large.
large
ist nicht negativ.
small
überschreitet nicht die Größe einer kleinen Integer-Zahl (MAXSMALL
).
Zeile 23 berechnet den Wert von small
modulo 5.000. Der Wert in small
bleibt dabei
unverändert. Der Ausdruck liefert das Ergebnis 0, wenn small
ein genaues Vielfaches
von 5.000 ist. In diesem Fall gibt das Programm als Fortschrittskontrolle einen Punkt
auf dem Bildschirm aus. Zeile 26 inkrementiert den Wert von small
, während Zeile 28
den Wert von large
um 2 dekrementiert.
Wenn irgendeine der drei Bedingungen in der while
-Schleife nicht erfüllt ist, endet die
Schleife, und die Ausführung des Programms setzt sich nach der schließenden Klammer
der while
-Schleife in Zeile 29 fort.
Der Modulo-Operator (
%
) und komplexe Bedingungen wurden am Tag 3, »Variablen und Konstanten«, besprochen.
Manchmal soll das Programm an den Anfang einer while
-Schleife zurückkehren, bevor
die gesamte Gruppe von Anweisungen in der while
-Schleife abgearbeitet ist. Die
continue
-Anweisung bewirkt einen Sprung zurück an den Beginn der Schleife.
Es kann auch sein, daß man die Schleife verlassen muß, bevor die Abbruchbedingung
erfüllt ist. Die break
-Anweisung führt unmittelbar zum Austritt aus der while
-Schleife,
und die Programmausführung wird nach der schließenden geschweiften Klammer fortgesetzt.
Listing 7.4 demonstriert die Verwendung dieser Anweisungen. Dieses Mal ist das
Spiel etwas komplizierter. Der Anwender wird aufgefordert, eine kleine Zahl (small
),
eine große Zahl (large
), eine Sprungzahl (skip
) und eine Zielzahl (target
) einzugeben.
Das Programm inkrementiert die Zahl small
um 1 und dekrementiert die Zahl large
um 2. Das Dekrementieren wird übersprungen, wenn small
ein Vielfaches der Zahl
skip
ist. Das Spiel endet, sobald small
größer als large
ist. Wenn die Zahl large
genau
die Zielzahl target
trifft, erscheint eine Mitteilung, und das Spiel stoppt.
Der Anwender muß versuchen, eine Zielzahl für die Zahl large
einzugeben, die das
Spiel stoppt.
Listing 7.4: Die Anweisungen break und continue
1: // Listing 7.4
2: // Demonstriert die Anweisungen break und continue
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: unsigned short small;
9: unsigned long large;
10: unsigned long skip;
11: unsigned long target;
12: const unsigned short MAXSMALL=65535;
13:
14: cout << "Bitte eine kleine Zahl eingeben: ";
15: cin >> small;
16: cout << "Bitte eine grosse Zahl eingeben: ";
17: cin >> large;
18: cout << "Bitte eine Sprungzahl eingeben: ";
19: cin >> skip;
20: cout << "Bitte eine Zielzahl eingeben: ";
21: cin >> target;
22:
23: cout << "\n";
24:
25: // 3 Abbruchbedingungen für die Schleife einrichten
26: while (small < large && large > 0 && small < MAXSMALL)
27:
28: {
29:
30: small++;
31:
32: if (small % skip == 0) // Dekrementieren überspringen?
33: {
34: cout << "Ueberspringen " << small << endl;
35: continue;
36: }
37:
38: if (large == target) // target genau getroffen?
39: {
40: cout << "Ziel getroffen!";
41: break;
42: }
43:
44: large-=2;
45: } // Ende der while-Schleife
46:
47: cout << "\nKlein: " << small << " Gross: " << large << endl;
48: return 0;
48: }
Bitte eine kleine Zahl eingeben: 2
Bitte eine grosse Zahl eingeben: 20
Bitte eine Sprungzahl eingeben: 4
Bitte eine Zielzahl eingeben: 6
Ueberspringen 4
Ueberspringen 8
Klein: 10 Gross: 8
In diesem Spiel hat der Anwender verloren. small
wurde größer als large
, bevor er die
target
-Zahl von 6 erreicht hat.
In Zeile 26 steht der Test der while
-Bedingungen. Wenn small
weiterhin kleiner als
large
ist, large
größer als 0 ist und small
noch nicht den Maximalwert für eine kleine
int
-Zahl überschritten hat, tritt die Programmausführung in den Rumpf der Schleife
ein.
Die Anweisung in Zeile 32 berechnet den Rest der Ganzzahldivision von small
und
skip
. Wenn small
ein Vielfaches von skip
ist, wird die continue
-Anweisung erreicht,
und die Programmausführung springt an den Beginn der Schleife in Zeile 26. Damit
übergeht das Programm den Test auf target
und das Dekrementieren von large
.
Zeile 38 testet target
erneut gegen den Wert für large
. Sind beide Werte gleich, hat
der Anwender gewonnen. Es erscheint eine Meldung, und das Programm erreicht die
break
-Anweisung. Das bewirkt einen sofortigen Austritt aus der while
-Schleife, und
die Programmausführung wird mit Zeile 46 fortgesetzt.
Sowohl
continue
als auchbreak
sollte man mit Umsicht einsetzen. Nachgoto
sind es die beiden gefährlichsten Befehle, und zwar aus den gleichen Gründen wie beigoto
angeführt. Programme, die plötzlich die Richtung ändern, sind schwerer zu verstehen, und der großzügige Einsatz voncontinue
undbreak
macht sogar eine kleinewhile
-Schleifenkonstruktion unverständlich.
Die Anweisung
continue
; bewirkt, daß einewhile
- oderfor
-Schleife wieder zum Anfang der Schleife zurückkehrt. In Listing 7.4 finden Sie ein Beispiel für den Einsatz voncontinue
.
Die
break
-Anweisung bewirkt den direkten Ausstieg aus einewhile
- oderfor
-Schleife. Die Programmausführung springt zu der schließenden geschweiften Klammer.while (bedingung)
{
if (bedingung2)
break;
// anweisungen;
}
Bei der in einer while
-Schleife getesteten Bedingung kann es sich um einen beliebigen
gültigen C++-Ausdruck handeln. Solange die Bedingung true
bleibt, wird die while
-
Schleife fortgesetzt. Man kann eine Endlosschleife erzeugen, indem man für die zu
testende Bedingung den Wert true
angibt. Listing 7.5 realisiert mit Hilfe dieser Konstruktion
einen Zähler bis 10.
Listing 7.5: while(true)-Schleifen
1: // Listing 7.5
2: // Demonstriert eine while-true-Schleife
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int counter = 0;
9:
10: while (true)
11: {
12: counter ++;
13: if (counter > 10)
14: break;
15: }
16: cout << "Zaehler: " << counter << "\n";
17: return 0;
18: }
Zähler: 11
Zeile 10 richtet eine while
-Schleife mit einer Bedingung ein, die niemals false
liefern
kann. Die Schleife inkrementiert in Zeile 12 die Zählervariable (counter
) und testet
dann in Zeile 13, ob counter
den Wert 10 überschritten hat. Ist das nicht der Fall,
führt die Schleife einen erneuten Durchlauf aus. Wenn counter
größer als 10 ist, beendet
die break
-Anweisung in Zeile 14 die while
-Schleife, und die Programmausführung
geht direkt zu Zeile 16, wo die Ausgabe der Ergebnisse stattfindet.
Dieses Programm funktioniert zwar, ist aber nicht sehr elegant formuliert - ein gutes
Beispiel für das falsche Werkzeug. Das gleiche kann man realisieren, wenn man den
Test des Zählerwertes dorthin schreibt, wo er hingehört: in die while
-Bedingung.
Endlosschleifen wie
while(true)
können dazu führen, daß sich der Computer aufhängt, wenn die Abbruchbedingung niemals erreicht wird. Verwenden Sie diese Konstruktion mit Vorsicht, und führen Sie gründliche Tests durch.
C++ bietet verschiedene Möglichkeiten, um die gleiche Aufgabe zu realisieren. Das eigentliche Kunststück ist es, das richtige Werkzeug für die jeweilige Aufgabe herauszusuchen.
Es kann sein, daß der Rumpf einer while
-Schleife gar nicht zur Ausführung gelangt.
Die while
-Anweisung prüft die Bedingung vor allen Anweisungen. Liefert die Bedingung
false
, überspringt das Programm den gesamten Rumpf der Schleife. Listing 7.6
verdeutlicht das.
Listing 7.6: Den Rumpf der while-Schleife überspringen
1: // Listing 7.6
2: // Den Rumpf der while-Schleife überspringen, wenn
3: // die Bedingung false ist.
4:
5: #include <iostream.h>
6:
7: int main()
8: {
9: int counter;
10: cout << "Wie viele Hallos?: ";
11: cin >> counter;
12: while (counter > 0)
13: {
14: cout << "Hallo!\n";
15: counter--;
16: }
17: cout << "Zaehler ist: " << counter;
18: return 0;
19: }
Wie viele Hallos?: 2
Hallo!
Hallo!
Zähler ist: 0
Wie viele Hallos?: 0
Zähler ist Ausgabe: 0
Die Anweisung in Zeile 10 fordert den Anwender zur Eingabe eines Startwerts auf,
der in der Integer-Variablen counter
gespeichert wird. Der Wert in der Variablen wird
in Zeile 12 überprüft und im Rumpf der while
-Schleife dekrementiert. Beim ersten
Programmdurchlauf hat der Anwender für counter
die Zahl 2 eingegeben, so daß die
Schleife zweimal ausgeführt wurde. Beim zweiten Mal hat er allerdings eine 0 eingegeben.
Beim Test von counter
in Zeile 12 liefert die Bedingung false
, da counter
nicht
größer als 0 ist. Damit überspringt das Programm die gesamte while
-Schleife, und die
Meldung »Hallo!« erscheint überhaupt nicht.
Wie kann man nun sicherstellen, daß »Hallo!« wenigstens einmal zu sehen ist? Die
while
-Schleife kann das nicht realisieren, da der Test der Bedingung vor jeglicher Ausgabe
erfolgt. Man kann die Schleifenausführung erzwingen, indem man eine if
-Anweisung
unmittelbar vor dem Eintritt in die Schleife einbaut:
if (counter < 1) // Einen Minimalwert erzwingen
counter = 1;
Das ist aber eine häßliche und wenig elegante Lösung.
Die do...while
-Schleife führt den Rumpf der Schleife aus, bevor der Test der Bedingung
stattfindet. Damit ist gesichert, daß der Rumpf mindestens einmal abgearbeitet
wird. Listing 7.7 zeigt das umgeschriebene Programm von Listing 7.6 mit einer
do...while
-Schleife.
Listing 7.7: Demonstration einer do..while-Schleife
1: // Listing 7.7
2: // Demonstriert eine do while-Schleife
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int counter;
9: cout << "Wie viele Hallos? ";
10: cin >> counter;
11: do
12: {
13: cout << "Hallo\n";
14: counter--;
15: } while (counter >0 );
16: cout << "Zaehler ist: " << counter << endl;
17: return 0;
18: }
Wie viele Hallos? 2
Hallo
Hallo
Zähler ist: 0
Die Anweisung in Zeile 9 fordert den Anwender zur Eingabe eines Startwerts auf, der
in der Integer-Variablen counter
gespeichert wird. Das Programm tritt in die
do...while
-Schleife ein, bevor der Test der Bedingung erfolgt. Damit ist die Ausführung
der Schleife mindestens einmal garantiert. In Zeile 13 steht die Ausgabe der Meldung,
Zeile 14 dekrementiert den Zähler, und in Zeile 15 findet der Test der Bedingung
statt. Wenn die Bedingung den Wert true
ergibt, springt die Ausführung an den
Anfang der Schleife in Zeile 13, andernfalls geht es direkt zu Zeile 16.
Die Anweisungen continue
und break
arbeiten in der do...while
-Schleife genauso wie
in der while
-Schleife. Der einzige Unterschied zwischen einer while
- und einer
do...while
-Schleife besteht im Zeitpunkt für den Test der Bedingung.
Die Syntax für die
do...while
-Anweisung lautet:do
anweisung
while (bedingung);\Erst wird
anweisung
ausgeführt und dannbedingung
getestet. Ergibtbedingung
true
, wird die Schleife wiederholt, ansonsten endet die Schleife. Die Anweisungen und Bedingungen entsprechen denen derwhile
-Schleife.// bis 10 zaehlen
in x = 0;
do
cout << "X: " << x++;
while (x < 10)// Alphabet in Kleinbuchstaben ausgeben
char ch = 'a';
do
{
cout << ch << ' ';
ch++;
} while ( ch <= 'z' );
Bei der Programmierung von while
-Schleifen stellt man häufig fest, daß man eine
Startbedingung festlegt, eine Bedingung auf true
testet und eine Variable bei jedem
Schleifendurchlauf inkrementiert oder anderweitig verändert. Listing 7.8 zeigt dazu
ein Beispiel.
Listing 7.8: Untersuchung einer while-Schleife
1: // Listing 7.8
2: // Schleifendurchlaeufe mit while
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int counter = 0;
9:
10: while(counter < 5)
11: {
12: counter++;
13: cout << "Schleife! ";
14: }
15:
16: cout << "\nZaehler: " << counter << ".\n";
17: return 0;
18: }
Schleife! Schleife! Schleife! Schleife! Schleife!
Zähler: 5.
Zeile 8 initialisiert die Variable counter
für die Anfangsbedingung mit 0. Der Test in
Zeile 10 prüft, ob counter
kleiner als 5 ist. In Zeile 12 wird counter
inkrementiert. Zeile
13 gibt nur eine einfache Meldung aus - hier sind aber auch wichtigere Aufgaben
denkbar, die bei jedem Inkrementieren von counter
zu erledigen sind.
Eine for
-Schleife faßt die drei Schritte Initialisierung, Test und Inkrementierung in einer
Anweisung zusammen. Die Syntax der for
-Anweisung besteht aus dem Schlüsselwort
for,
gefolgt von einem Klammernpaar. Innerhalb dieser Klammern befinden
sich drei durch Semikolons getrennte Anweisungen.
Die erste Anweisung ist die Initialisierung. Hier kann man jede zulässige C++-Anweisung
angeben. Normalerweise verwendet man diese Anweisung aber, um eine Zählervariable
zu erzeugen und zu initialisieren. Die zweite Anweisung realisiert den Test und
kann ebenfalls jeder zulässige C++-Ausdruck sein. Diese Anweisung übernimmt die
Rolle der Bedingung in der while
-Schleife. Anweisung drei ist die Aktion. Normalerweise
inkrementiert oder dekrementiert man einen Wert, obwohl auch alle zulässigen
C++-Anweisungen möglich sind. Beachten Sie, daß zwar die erste und dritte Anweisung
alle in C++ zulässigen Anweisungen sein können, daß aber für die zweite Anweisung
ein Ausdruck erforderlich ist - eine C++-Anweisung, die einen Wert zurückgibt.
Listing 7.9 demonstriert die for
-Schleife.
Listing 7.9: Beispiel für eine for-Schleife
1: // Listing 7.9
2: // Schleifendurchlaeufe mit for
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int counter;
9: for (counter = 0; counter < 5; counter++)
10: cout << "Schleife! ";
11:
12: cout << "\nZaehler: " << counter << ".\n";
13: return 0;
14: }
Schleife! Schleife! Schleife! Schleife! Schleife!
Zähler: 5.
Die for
-Anweisung in Zeile 9 kombiniert die Initialisierung von counter
, den Test, ob
counter
kleiner als 5 ist, und die Inkrementierung von counter
in einer Zeile. Der
Rumpf der for
-Anweisung steht in Zeile 10. Natürlich könnte man hier genausogut einen
Block vorsehen.
Die Syntax für die
for
-Anweisung lautet:for (initialsierung; test; aktion)
anweisung;Die Initialisierungsanweisung dient dazu, einen Zähler zu initialisieren oder auf andere Art und Weise die
for
-Schleife einzuleiten. Beitest
handelt es sich um einen beliebigen C++-Ausdruck, der bei jedem Schleifendurchlauf geprüft wird. Ergibttest
den Werttrue
, wird zuerst der Rumpf derfor
-Schleife und anschließend die Aktion
im Kopf derfor
-Schleife (normalerweise Inkrement des Zählers) ausgeführt.// Hallo 10mal ausgeben
for (int i = 0; i < 10; i++)
cout << "Hallo! ";for (int i = 0; i < 10; i++)
{
cout << "Hallo!" << endl;
cout << "der Wert von i beträgt: " << i << endl;
}
for
-Anweisungen sind leistungsfähig und flexibel. Die drei unabhängigen Anweisungen
(Initialisierung, Test und Aktion) führen von selbst zu einer Reihe von Varianten.
Eine for
-Schleife arbeitet nach folgendem Schema:
true
ergibt: Ausführen der Schleife und dann der Aktionsanweisung.
Nach jedem Schleifendurchlauf wiederholt die for
-Schleife die Schritte 2 und 3.
Es ist durchaus üblich, mehrere Variablen auf einmal zu initialisieren, einen zusammengesetzten logischen Ausdruck zu testen und mehrere Anweisungen auszuführen. Die Anweisungen für Initialisierung und Aktion lassen sich durch mehrere C++-Anweisungen ersetzen, die jeweils durch Komma zu trennen sind. Listing 7.10 zeigt die Initialisierung und Inkrementierung von zwei Variablen.
Listing 7.10: Mehrere Anweisungen in for-Schleifen
1: // Listing 7.10
2: // Demonstriert mehrere Anweisungen in
3: // for-Schleifen
4:
5: #include <iostream.h>
6:
7: int main()
8: {
9: for (int i=0, j=0; i<3; i++, j++)
10: cout << "i: " << i << " j: " << j << endl;
11: return 0;
12: }
i: 0 j: 0
i: 1 j: 1
i: 2 j: 2
Zeile 9 initialisiert die beiden Variablen i
und j
mit dem Wert 0. Die Auswertung der
Testbedingung (i<3
) liefert true
. Somit führt die for
-Konstruktion die Anweisungen im
Rumpf aus: hier die Ausgabe der Werte. Schließlich wird die dritte Klausel in der for
-
Anweisung ausgeführt: Inkrementieren von i
und j
.
Nach Abarbeitung von Zeile 10 wertet die for
-Konstruktion die Bedingung erneut
aus. Liefert die Auswertung weiterhin true
, wiederholt die for
-Schleife die Aktionen
(Inkrementieren von i
und j
) und führt den Rumpf der Schleife erneut aus. Das setzt
sich so lange fort, bis der Test false
ergibt. Die Aktionsanweisung gelangt dann nicht
mehr zur Ausführung, und der Programmablauf setzt sich nach der Schleife fort.
In einer for
-Schleife können einige oder alle Anweisungen leer sein. Dazu markiert
man mit einem Semikolon die Stelle, wo die Anweisung normalerweise steht. Um
eine for
-Schleife zu erzeugen, die genau wie eine while
-Schleife arbeitet, läßt man die
ersten und dritten Anweisungen weg. Dazu zeigt Listing 7.11 ein Beispiel.
Listing 7.11: Eine for-Schleife mit leeren Anweisungen
1: // Listing 7.11
2: // for-Schleife mit leeren Anweisungen
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int counter = 0;
9:
10: for( ; counter < 5; )
11: {
12: counter++;
13: cout << "Schleife! ";
14: }
15:
16: cout << "\nZaehler: " << counter << ".\n";
17: return 0;
18: }
Schleife! Schleife! Schleife! Schleife! Schleife!
Zähler: 5.
Ein Vergleich mit der weiter vorn in Listing 7.8 gezeigten while
-Schleife läßt eine nahezu
identische Konstruktion erkennen. Die Initialisierung der Zählervariablen erfolgt
in Zeile 8. Die for
-Anweisung in Zeile 10 initialisiert keinerlei Werte, enthält aber einen
Test für counter < 5
. Weiterhin fehlt eine Inkrement-Anweisung, so daß sich diese
Schleife wie die folgende Konstruktion verhält:
while (counter < 5)
Auch hier bietet C++ verschiedene Möglichkeiten, dasselbe zu verwirklichen. Kein erfahrener
C++-Programmierer würde eine for
-Schleife auf diese Weise verwenden.
Das Beispiel verdeutlicht aber die Flexibilität der for
-Anweisung. In der Tat ist es mit
break
und continue
möglich, eine for
-Schleife mit keiner der drei Anweisungen zu
realisieren. Listing 7.12 zeigt dazu ein Beispiel.
Listing 7.12: Eine leere for-Schleifenanweisung
1: //Listing 7.12 zeigt eine
2: //leere for-Schleifenanweisung
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int counter=0; // Initialisierung
9: int max;
10: cout << "Wie viele Hallos? ";
11: cin >> max;
12: for (;;) // Endlosschleife mit for
13: {
14: if (counter < max) // Test
15: {
16: cout << "Hallo!\n";
17: counter++; // Inkrementieren
18: }
19: else
20: break;
21: }
22: return 0;
23: }
Wie viele Hallos? 3
Hallo!
Hallo!
Hallo!
Damit hat man die for
-Schleife bis zu ihrem absoluten Limit ausgereizt. Initialisierung,
Test und Aktion wurden gänzlich aus der for
-Anweisung herausgenommen. Die Initialisierung
findet man in Zeile 8, bevor die for
-Schleife überhaupt beginnt. Der Test erfolgt
in einer separaten if
-Anweisung in Zeile 14. Verläuft er erfolgreich, wird die Aktion
- Inkrementieren von counter
- in Zeile 17 ausgeführt. Wenn der Test scheitert,
verläßt das Programm die Schleife mit der Anweisung in Zeile 20.
Wenn dieses Programm auch etwas absurd erscheint, kann es durchaus sein, daß man
genau eine Schleife im Stil von for(;;)
oder while(true)
braucht. Ein Beispiel für einen
sinnvolleren Einsatz derartiger Schleifen bei switch
-Anweisungen folgt weiter hinten
in diesem Kapitel.
Der Kopf einer for
-Anweisung bietet sehr viel Spielraum, so daß man manchmal auf
einen Rumpf gänzlich verzichten kann. In diesem Fall muß man eine leere Anweisung
(;
) als Rumpf der Schleife vorsehen. Das Semikolon darf auf derselben Zeile wie der
Kopf stehen, obwohl man es an dieser Stelle leicht übersehen kann. Listing 7.13 zeigt
dazu ein Beispiel.
Listing 7.13: Darstellung einer leeren Anweisung als Rumpf einer for-Schleife
1: // Listing 7.13
2: // Zeigt eine leere Anweisung als Rumpf
3: // einer for-Schleife
4:
5: #include <iostream.h>
6: int main()
7: {
8: for (int i = 0; i<5; cout << "i: " << i++ << endl)
9: ;
10: return 0;
11: }
i: 0
i: 1
i: 2
i: 3
i: 4
Die for
-Schleife in Zeile 8 enthält drei Anweisungen: Die Initialisierungsanweisung
richtet den Zähler i
ein und initialisiert ihn zu 0. Die Bedingungsanweisung testet auf
i<5
, und die Aktionsanweisung gibt den Wert in i
aus und inkrementiert ihn.
Im Rumpf der for
-Schleife selbst bleibt nichts weiter zu tun, so daß man hier eine leere
Anweisung (;
) schreibt. Es sei darauf hingewiesen, daß diese Konstruktion nicht die
beste Lösung darstellt. Die Aktionsanweisung ist zu umfangreich. Die folgende Version
ist besser geeignet:
8: for (int i = 0; i<5; i++)
9: cout << "i: " << i << endl;
Beide Versionen bewirken exakt dasselbe, das zweite Beispiel ist aber leichter zu verstehen.
Befindet sich eine Schleife im Rumpf einer anderen Schleife, spricht man vom Verschachteln
von Schleifen. Die innere Schleife wird bei jedem Durchlauf der äußeren
vollständig abgearbeitet. Listing 7.14 zeigt ein Beispiel, das Markierungen in einer
Matrix mit Hilfe verschachtelter for
-Schleifen schreibt.
Listing 7.14: Verschachtelte for-Schleifen
1: // Listing 7.14
2: // Zeigt verschachtelte for-Schleifen
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: int rows, columns;
9: char theChar;
10: cout << "Wie viele Zeilen? ";
11: cin >> rows;
12: cout << "Wie viele Spalten? ";
13: cin >> columns;
14: cout << "Welches Zeichen? ";
15: cin >> theChar;
16: for (int i = 0; i<rows; i++)
17: {
18: for (int j = 0; j<columns; j++)
19: cout << theChar;
20: cout << "\n";
21: }
22: return 0;
23: }
Wie viele Zeilen? 4
Wie viele Spalten? 12
Welches Zeichen? x
xxxxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxx
xxxxxxxxxxxx
Der Anwender wird aufgefordert, die Anzahl der Zeilen (rows
) und Spalten (columns
)
und ein auszugebendes Zeichen einzugeben. Die erste Schleife in Zeile 16 initialisiert
einen Zähler i
mit 0. Dann beginnt die Ausführung der äußeren Schleife.
In Zeile 18, der ersten Zeile im Rumpf der äußeren for
-Schleife, wird eine weitere
for
-Schleife eingerichtet. Ein zweiter Zähler (j
) wird mit 0 initialisiert, und der Rumpf
der inneren for
-Schleife wird ausgeführt. In Zeile 19 erfolgt die Ausgabe des gewählten
Zeichens, und die Steuerung kehrt zum Kopf der inneren for
-Schleife zurück. Beachten
Sie, daß die innere for
-Schleife nur aus einer Anweisung besteht (der Ausgabe
des Zeichens). Ergibt der Test der Bedingung j<columns
das Ergebnis true
, wird j
inkrementiert
und das nächste Zeichen ausgegeben. Das setzt sich fort, bis j
gleich der
Anzahl der Spalten (columns
) ist.
Sobald der Test der inneren Schleife den Wert false
liefert, in diesem Beispiel nach
Ausgabe von zwölf x-Zeichen, springt die Ausführung direkt zu Zeile 20, und eine
neue Zeile wird begonnen. Die äußere for
-Schleife kehrt nun zu ihrem Kopf zurück,
wo die Bedingung i<rows
getestet wird. Ergibt dieser Test true
, wird i
inkrementiert
und der Rumpf der Schleife ausgeführt.
Im zweiten Durchlauf der äußeren for
-Schleife beginnt die innere for
-Schleife von
neuem. Damit erhält j
erneut den Anfangswert 0, und wieder wird die gesamte innere
Schleife ausgeführt.
Dem Programm liegt der Gedanke zugrunde, daß in einer verschachtelten Schleife die
innere Schleife für jeden Durchlauf der äußeren Schleife ausgeführt wird. Die Anzahl
der ausgegebenen Zeichen pro Zeile entspricht damit dem Wert in columns
.
Nebenbei bemerkt: Viele C++-Programmierer verwenden die Buchstaben
i
undj
als Zählervariablen. Diese Tradition ist auf FORTRAN zurückzuführen, da dort die Buchstabeni
,j
,k
,l
,m
undn
die einzigen gültigen Zählervariablen waren.
In der Vergangenheit erstreckte sich der Gültigkeitsbereich von Variablen, die in einer
for
-Schleife deklariert wurden, über den gesamten äußeren Block. Gemäß dem neuen
ANSI-Standard wird dies nun anders geregelt. Jetzt beschränkt sich der Gültigkeitsbereich
dieser Variablen auf den for
-Schleifenblock. Doch diese Änderung wird noch
nicht von allen Compilern unterstützt. Mit folgendem Code können Sie Ihren Compiler
testen:
#include <iostream.h>
int main()
{
// i gueltig für die for Schleife?
for (int i = 0; i<5; i++)
{
cout << "i: " << i << endl;
}
i = 7; // sollte nicht im Gueltigkeitsbereich liegen!
return 0;
}
Erfolgt die Kompilierung ohne Fehlermeldung, wird der geänderte ANSI-Standard in dieser Hinsicht noch nicht unterstützt.
Merkt Ihr Compiler, daß i
noch nicht definiert worden ist (in der Zeile i=7), dann wird
der neue Standard von Ihrem Compiler bereits berücksichtigt. Wenn Sie Ihren Code
wie folgt umändern, läßt er sich auf beiden Compilern fehlerfrei kompilieren:
#include <iostream.h>
int main()
{
int i; //ausserhalb der for-Schleife deklariert
for (int i = 0; i<5; i++)
{
cout << "i: " << i << endl;
}
i = 7; // Gueltigkeitsbereich korrekt für alle Compiler
return 0;
}
In Kapitel 5, »Funktionen«, habe ich Ihnen gezeigt, wie Sie das Problem der Fibonacci- Reihe mit Hilfe der Rekursion lösen können. Zur Erinnerung: Eine Fibonacci-Reihe beginnt mit 1,1,2,3, und alle folgenden Zahlen sind Summen der zwei vorhergehenden:
Die n-te Fibonacci-Zahl ist demnach die Summe der Fibonacci-Zahlen n-1 und n-2. In Kapitel 5 haben wir das Problem, für eine bestimmte Fibonacci-Zahl in der Reihe einen Wert zu errechnen, mit Hilfe von Rekursion gelöst. Das Listing 7.15 löst das Problem mittels Iteration.
Listing 7.15: Den Wert einer Fibonacci-Zahl mittels Iteration ermitteln
1: // Listing 7.15
2: // zeigt wie der Wert der n-ten Fibonacci-Zahl
3: // mittels Iteration ermittelt wird
4:
5: #include <iostream.h>
6:
8:
9: int fib(int position);
10: int main()
11: {
12: int answer, position;
13: cout << "Welche Position? ";
14: cin >> position;
15: cout << "\n";
16:
17: answer = fib(position);
18: cout << answer << " lautet der Wert der ";
19: cout << position << "ten Fibonacci-Zahl.\n";
20: return 0;
21: }
22:
23: int fib(int n)
24: {
25: int minusTwo=1, minusOne=1, answer=2;
26:
27: if (n < 3)
28: return 1;
29:
30: for (n -= 3; n; n--)
31: {
32: minusTwo = minusOne;
33: minusOne = answer;
34: answer = minusOne + minusTwo;
35: }
36:
37: return answer;
38: }
Welche Position? 4
3 lautet der Wert der 4ten Fibonacci-Zahl.
Welche Position? 5
5 lautet der Wert der 5ten Fibonacci-Zahl.
Welche Position? 20
6765 lautet der Wert der 20sten Fibonacci-Zahl.
Welche Position? 100
3314859971 lautet der Wert der 100sten Fibonacci-Zahl.
In Listing 7.15 wird die Fibonacci-Reihe statt durch Rekursion mit Hilfe einer Iteration gelöst. Dieser Ansatz ist schneller und verbraucht wesentlich weniger Speicherplatz als die rekursive Lösung.
Zeile 13 fragt den Anwender nach einer Position, deren Wert ermittelt werden soll.
Es ergeht ein Aufruf an die Funktion fib()
, die die Position testet. Ist die Position kleiner
als 3, liefert die Funktion den Wert 1
zurück. Ab der Position 3 iteriert die Funktion
mit folgendem Algorithmus:
answer
mit 2, minusTwo
mit 1 und minusOne
mit 1. Dekrementiere die Position um 3, da die ersten zwei Zahlen von der Startposition abgefangen werden.
a. der aktuelle Wert von
minusOne
inminusTwo
abgelegt wird,
c. minusOne
undminusTwo
addiert und die Summe inanswer
abgelegt wird,
n
den Wert 0
erreicht, gib die Antwort aus.
Auf diese Art und Weise würden Sie das Problem auch mit Papier und Bleistift lösen. Angenommen Sie sollen den Wert der fünften Fibonacci-Zahl ermitteln. Zuerst würden Sie schreiben:
und denken: »Nur noch zwei.« Dann würden Sie 2+1 addieren, 3 hinschreiben und denken: »Eine Zahl fehlt noch.« Anschließend würden Sie 3+2 schreiben und die Antwort wäre 5. Dabei machen Sie nichts anderes, als bei jedem Durchlauf Ihre Aufmerksamkeit um eine Zahl nach rechts zu rücken und die Zahl, deren Wert gesucht wird, um 1 zu dekrementieren.
Bedenken Sie die Bedingung, die in Zeile 30 getestet wird (n
). Dabei handelt es sich
um ein C++-Idiom, das n != 0
entspricht. Diese for
-Schleife basiert auf der Annahme,
daß n
zu false
getestet wird, wenn es den Wert 0 enthält. Der Kopf der for
-
Schleife hätte auch wie folgt geschrieben werden können
for (n-=3; n!=0; n--)
was vielleicht eindeutiger gewesen wäre. Dieses Idiom ist jedoch so geläufig in C++, daß es keinen Zweck hat, sich dagegen aufzulehnen.
Kompilieren, linken und starten Sie das Programm zusammen mit dem Programm aus Kapitel 5, das Ihnen eine rekursive Lösung zu dem Problem anbietet. Versuchen Sie einmal, den Wert der 25. Zahl herauszufinden, und vergleichen Sie die Zeit, die jedes Programm benötigt. Rekursion ist zwar eine elegante Lösung, doch da die Funktionsaufrufe auf Kosten der Ausführungszeit gehen und es so viele Funktonsaufrufe bei der Rekursion gibt, ist die Ausführung deutlich langsamer als bei der Iteration.
Microcomputer sind normalerweise für arithmetische Operationen optimal ausgelegt, deshalb sollte der iterative Weg rasend schnell zur Lösung führen.
Sein Sie vorsichtig und geben Sie keine zu große Zahl ein. fib
wird sehr schnell sehr
groß, und auch Integer vom Typ long
laufen irgendwann über.
In Kapitel 4 haben Sie gelernt, wie man if
-Anweisungen und else...if
-Anweisungen
schreibt. Da man bei zu tief verschachtelten Konstruktionen schnell Gefahr läuft, den
Überblick zu verlieren, bietet C++ für solche Konstruktionen eine Alternative. Im Gegensatz
zur if
-Anweisung, die lediglich einen Wert auswertet, lassen sich mit switch
-
Anweisungen Verzweigungen in Abhängigkeit von mehreren unterschiedlichen Werten
aufbauen. Die allgemeine Form der switch
-Anweisung lautet:
switch (Ausdruck)
{
case Wert1: Anweisung;
break;
case Wert2: Anweisung;
break;
....
case WertN: Anweisung;
break;
default: Anweisung;
}
Ausdruck
ist jeder gültige C++-Ausdruck. Anweisung
steht für beliebige C++-Anweisungen
oder Blöcke von Anweisungen, die zu einem Integer-Wert ausgewertet oder zweifelsfrei
in einen Integer-Wert konvertiert werden können. Beachten Sie, daß die Auswertung
nur auf Gleichheit erfolgt. Man kann weder relationale noch Boole'sche
Operatoren verwenden.
Wenn einer der case
-Werte mit dem Ausdruck übereinstimmt, springt die Ausführung
zu diesen Anweisungen und arbeitet sie bis zum Ende des switch
-Blocks oder bis zur
nächsten break
-Anweisung ab. Läßt sich keine Übereinstimmung ermitteln, verzweigt
die Ausführung zur optionalen default
-Anweisung. Ist kein default
-Zweig vorhanden
und gibt es keinen übereinstimmenden Wert, kommt es überhaupt nicht zur Abarbeitung
von Anweisungen in der switch
-Konstruktion - die Anweisung ist damit beendet.
Es empfiehlt sich, in
switch
-Anweisungen immer einendefault
-Zweig vorzusehen. Auch wenn man diesen Zweig eigentlich nicht benötigt, kann man hier zumindest auf angeblich unmöglichecase
-Fälle testen und eine Fehlermeldung ausgeben. Damit läßt sich die Fehlersuche oft erheblich beschleunigen.
Wichtig ist besonders die break
-Anweisung am Ende eines case
-Zweiges. Wenn diese
fehlt, geht das Programm zur Ausführung des nächsten case
-Zweiges über. Manchmal
ist das zwar gewollt, gewöhnlich handelt es sich aber um einen Fehler. Falls Sie die
Programmausführung tatsächlich mit dem nächsten case
-Zweig fortsetzen möchten,
sollten Sie mit einem Kommentar darauf hinweisen, daß die break
-Anweisung nicht
vergessen wurde.
Listing 7.16 zeigt ein Beispiel für die switch
-Anweisung.
Listing 7.16: Einsatz der switch-Anweisung
1: // Listing 7.16
2: // Zeigt die switch-Anweisung
3:
4: #include <iostream.h>
5:
6: int main()
7: {
8: unsigned short int number;
9: cout << "Bitte eine Zahl zwischen 1 und 5 eingeben: ";
10: cin >> number;
11: switch (number)
12: {
13: case 0: cout << "Leider zu klein!";
14: break;
15: case 5: cout << "Gut!\n"; // Weiter mit naechstem case
16: case 4: cout << "Sehr gut!\n"; // Weiter mit naechstem case
17: case 3: cout << "Ausgezeichnet!\n"; // Weiter mit naechstem case
18: case 2: cout << "Meisterhaft!\n"; // Weiter mit naechstem case
19: case 1: cout << "Unglaublich!\n";
20: break;
21: default: cout << "Zu gross!\n";
22: break;
23: }
24: cout << "\n\n";
25: return 0;
26: }
Bitte eine Zahl zwischen 1 und 5 eingeben: 3
Ausgezeichnet!
Meisterhaft!
Unglaublich!
Bitte eine Zahl zwischen 1 und 5 eingeben: 8
Zu gross!
Das Programm fragt zunächst eine Zahl vom Anwender ab und wertet sie dann in der
switch
-Anweisung aus. Ist die Zahl gleich 0, stimmt das mit der case
-Anweisung in
Zeile 13 überein. Es erscheint die Meldung Leider zu klein!
, und die break
-Anweisung
beendet die switch
-Struktur. Wenn der Wert gleich 5 ist, springt die Programmausführung
zu Zeile 15, wo eine Meldung ausgegeben wird. Dann wird die Ausführung
mit Zeile 16 fortgesetzt, und es wird eine weitere Meldung ausgegeben. Das geht
so lange, bis die break
-Anweisung in Zeile 20 erreicht ist.
Im Endeffekt erscheinen bei Zahlen zwischen 1 und 5 mehrere Meldungen auf dem
Bildschirm. Wenn die Zahl nicht im Bereich zwischen 0 und 5 liegt, gilt sie im Programm
als zu groß. Diesen Fall behandelt die default
-Anweisung in Zeile 21.
Die Syntax der
switch
-Anweisung lautet wie folgt:switch (Ausdruck)
{
case Wert1: Anweisung;
case Wert2: Anweisung;
....
case WertN: Anweisung;
default: Anweisung;
}Mit der
switch
-Anweisung kann man Verzweigungen in Abhängigkeit von mehreren Werten des Ausdrucks aufbauen. Der Ausdruck wird ausgewertet, und wenn er einem dercase
-Werte entspricht, springt die Programmausführung in diese Zeile. Die Ausführung wird fortgesetzt, bis entweder das Ende derswitch
-Anweisung oder einebreak
-Anweisung erreicht wird.Stimmt der Ausdruck mit keinem der
case
-Zweige überein und es ist einedefault
-Anweisung vorhanden, springt die Programmausführung zu diesemdefault
-Zweig. Andernfalls endet dieswitch
-Anweisung.switch (choice)
{
case 0:
cout << "Null!" << endl;
break;
case 1:
cout << "Eins!" << endl;
break;
case 2:
cout << "Zwei!" << endl;
default:
cout << "Standard!" << endl;
}switch (choice)
{
case 0:
case 1:
case 2:
cout << "Kleiner als 3!";
break;
case 3:
cout << "Gleich 3!";
break;
default:
cout << "Groesser als 3!";
}
Mit Listing 7.17 kehren wir zurück zu den schon vorher besprochenen for
-Schleifen
(;;
). Diese Schleifen werden auch forever
-Schleifen oder Endlosschleifen genannt, da
sie endlos durchlaufen werden, bis das Programm auf ein break
trifft. Die forever
-
Schleife wird häufig verwendet, um Menüs einzurichten, den Anwender aufzufordern,
eine Auswahl zu treffen, auf die Auswahl zu reagieren und dann zu dem Menü zurückzukehren.
Diese Schleife wird fortgesetzt, bis der Anwender sich entscheidet, die
Schleife zu verlassen.
Einige Programmierer ziehen folgende Schreibweise vor
#define EVER ;;
for (EVER)
{
// Anweisungen ...
Eine forever
-Schleife ist eine Schleife ohne Abbruchbedingung. Schleifen dieser Art
kann man nur über eine break
-Anweisung verlassen. forever
-Schleifen werden auch
als Endlosschleifen bezeichnet.
Listing 7.17: Eine forever-Schleife
1: //Listing 7.17
2: //forever-Schleife zum Abfragen
3: //von Anwenderbefehlen
4: #include <iostream.h>
5:
6: // Prototypen
7: int menu();
8: void DoTaskOne();
9: void DoTaskMany(int);
10:
11: int main()
12: {
13:
14: bool exit = false;
15: for (;;)
16: {
17: int choice = menu();
18: switch(choice)
19: {
20: case (1):
21: DoTaskOne();
22: break;
23: case (2):
24: DoTaskMany(2);
25: break;
26: case (3):
27: DoTaskMany(3);
28: break;
29: case (4):
30: continue; // ueberfluessig!
31: break;
32: case (5):
33: exit=true;
34: break;
35: default:
36: cout << "Bitte erneut auswaehlen!\n";
37: break;
38: } // end switch
39:
40: if (exit)
41: break;
42: } // Ende von forever
43: return 0;
44: } // Ende von main()
45:
46: int menu()
47: {
48: int choice;
49:
50: cout << " **** Menue ****\n\n";
51: cout << "(1) Auswahl Eins.\n";
52: cout << "(2) Auswahl Zwei.\n";
53: cout << "(3) Auswahl Drei.\n";
54: cout << "(4) Menue erneut anzeigen.\n";
55: cout << "(5) Beenden.\n\n";
56: cout << ": ";
57: cin >> choice;
58: return choice;
59: }
60:
61: void DoTaskOne()
62: {
63: cout << "Aufgabe Eins!\n";
64: }
65:
66: void DoTaskMany(int which)
67: {
68: if (which == 2)
69: cout << "Aufgabe Zwei!\n";
70: else
71: cout << "Aufgabe Drei!\n";
72: }
**** Menue ****
(1) Auswahl Eins.
(2) Auswahl Zwei.
(3) Auswahl Drei.
(4) Menue erneut anzeigen.
(5) Beenden.
: 1
Aufgabe Eins!
**** Menue ****
(1) Auswahl Eins.
(2) Auswahl Zwei.
(3) Auswahl Drei.
(4) Menue erneut anzeigen.
(5) Beenden.
: 3
Aufgabe Drei!
**** Menue ****
(1) Auswahl Eins.
(2) Auswahl Zwei.
(3) Auswahl Drei.
(4) Menue erneut anzeigen.
(5) Beenden.
: 5
In diesem Programm fließen eine Reihe der heute und in den letzten Tagen vorgestellten
Konzepte zusammen. Daneben finden Sie auch einen häufigen Einsatz der
switch
-Anweisung.
In Zeile 15 startet eine forever
-Schleife. Die menu()
-Funktion wird aufgerufen. Sie gibt
das Menü auf dem Bildschirm aus und liefert die vom Anwender gewählte Option zurück.
Die switch
-Anweisung, die in Zeile 18 beginnt und in Zeile 38 endet, verzweigt
je nach der Auswahl des Anwenders.
Gibt der Anwender 1 ein, springt die Ausführung zu der Anweisung case 1:
in Zeile
20. Zeile 21 leitet die Ausführung dann zu der Funktion doTaskOne()
, die eine Meldung
ausgibt und zurückkehrt. Nachdem die Funktion zurückgekehrt ist, wird die Ausführung
in Zeile 22 fortgesetzt. Dort beendet der break
-Befehl die switch
-Anweisung,
und das Programm wird dann erst mit Zeile 39 fortgesetzt. Zeile 40 wertet die Variable
exit
aus. Ergibt sie true
, wird der break
-Befehl in Zeile 41 ausgeführt und die
for
-Schleife (;;
) wird verlassen. Ergibt sie hingegen false
, springt die Ausführung wieder
zurück zum Anfang der Schleife in Zeile 15.
Beachten Sie, daß die continue
-Anweisung in Zeile 30 an sich überflüssig ist. Hätte
man sie weggelassen, würde als nächstes die break
-Anweisung ausgeführt, die switch
-
Anweisung würde verlassen, exit
würde als false
ausgewertet, die Schleife würde erneut
durchlaufen und das Menü würde erneut ausgegeben. Mit continue
können Sie
jedoch den Test von exit
umgehen.
In C++ gibt es mehrere Möglichkeiten für die Realisierung von Schleifen. while
-
Schleifen prüfen eine Bedingung. Ergibt dieser Test das Ergebnis true
, werden die
Anweisungen im Rumpf der Schleife ausgeführt. do...while
-Schleifen führen den
Rumpf der Schleife aus und testen danach die Bedingung. for
-Schleifen initialisieren
einen Wert und testen dann einen Ausdruck. Wenn der Ausdruck gleich true
ist, werden
die letzte Anweisung im Kopf der for
-Schleife und ebenso der Rumpf der Schleife
ausgeführt. Nach jedem Durchlauf testet die Schleifenanweisung den Ausdruck erneut.
Auf die goto
-Anweisung verzichtet man im allgemeinen, da sie unbedingte Sprünge zu
einer scheinbar willkürlichen Stelle im Code ausführen kann, die einen unübersichtlichen
und schwer zu wartenden Code erzeugen. Mit der continue
-Anweisung kann
man while
-, do...while
- und for
-Schleifen sofort von vorn beginnen, während die
break
-Anweisung zum Verlassen von while
-, do....while
-, for
- und switch
-Anweisungen
führt.
Frage:
Wie wählt man zwischen if...else
und switch
aus?
Antwort:
Wenn mehr als ein oder zwei else
-Klauseln vorhanden sind und alle denselben
Ausdruck testen, sollte man eine switch
-Anweisung verwenden.
Frage:
Wie wählt man zwischen while
und
do...while
aus?
Antwort:
Wenn der Rumpf der Schleife zumindest einmal auszuführen ist, verwendet
man eine do...while
-Schleife. Andernfalls kann man mit einer while
-Schleife
arbeiten.
Frage:
Wie wählt man zwischen while
und for
aus?
Antwort:
Wenn man eine Zählvariable initialisiert, diese Variable testet und sie bei jedem
Schleifendurchlauf inkrementiert, kommt eine for
-Schleife in Frage. Ist
die Variable bereits initialisiert und wird nicht bei jedem Schleifendurchlauf
inkrementiert, kann eine while
-Schleife die bessere Wahl darstellen.
Frage:
Wie wählt man zwischen Rekursion und Iteration aus?
Antwort:
Einige Probleme fordern direkt eine Rekursion. Doch die meisten Probleme
lassen sich auch mittels Iteration lösen. Behalten Sie die Möglichkeit der Rekursion
im Hinterkopf, sie kann manchmal ganz nützlich sein.
Frage:
Ist es besser, while(true)
oder for (;;)
zu verwenden?
Antwort:
Hier gibt es keinen wesentlichen Unterschied.
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.
for
-Schleife?
goto
vermeiden?
for
-Schleife zu schreiben, deren Rumpf niemals ausgeführt wird?
while
-Schleifen in for
-Schleifen zu verschachteln?
x
, wenn die folgende for
-Schleife durchlaufen ist?
for (int x = 0; x < 100; x++)
for
-Schleife, die ein Muster von 10 x 10 Nullen (0) ausgibt.
for
-Anweisung, die in Zweierschritten von 100 bis 200 zählt.
while
-Schleife, die in Zweierschritten von 100 bis 200 zählt.
do...while
-Schleife, die in Zweierschritten von 100 bis 200 zählt.
int counter = 0;
while (counter < 10)
{
cout << "Zaehler: " << counter;
}
for (int counter = 0; counter < 10; counter++);
cout << counter << " ";
int counter = 100;
while (counter < 10)
{
cout << "Zaehler: " << counter;
counter--;
}
cout << "Geben Sie eine Zahl zwischen 0 und 5 ein: ";
cin >> theNumber;
switch (theNumber)
{
case 0:
doZero();
case 1: // Weiter mit nächstem case
case 2: // Weiter mit nächstem case
case 3: // Weiter mit nächstem case
case 4: // Weiter mit nächstem case
case 5:
doOneToFive();
break;
default:
doDefault();
break;
}
© Markt&Technik Verlag, ein Imprint der Pearson Education Deutschland GmbH