C++-Programmierung/ Einführung in C++/ Schleifen
Mit dem, was Sie bis jetzt gelernt haben, sollte es für Sie eine leichte Übung sein, die Zahlen von eins bis zehn ausgeben zu lassen. So könnte ein Programm aussehen, das dies tut:
1#include <iostream>
2
3int main() {
4 std::cout << "1\n";
5 std::cout << "2\n";
6 std::cout << "3\n";
7 std::cout << "4\n";
8 std::cout << "5\n";
9 std::cout << "6\n";
10 std::cout << "7\n";
11 std::cout << "8\n";
12 std::cout << "9\n";
13 std::cout << "10\n";
14}
11
22
33
44
55
66
77
88
99
1010
Dieses Programm ist einfach – aber was wäre, wenn die Zahlen eins bis einer Million ausgegeben werden sollen? Oder schlimmer noch – ja, es geht noch schlimmer: die Ausgabe hängt von der Benutzereingabe ab. Dann müssten Sie von der größtmöglichen Zahl ausgehen (bei
unsigned int
üblicherweise 4.294.967.295) und zusätzlich noch nach jeder Ausgabe überprüfen, ob die vom Benutzer eingegebene Zahl erreicht ist.
1#include <iostream>
2
3int main() {
4 unsigned i = 1; // 2 Variablen anlegen
5 unsigned benutzer = 0;
6
7 std::cin >> benutzer; // Benutzer gibt Zahl ein
8
9 if (i <= benutzer) { // Benutzereingabe erreicht?
10 std::cout << "1\n"; // Ausgabe der Zahl 1
11 ++i; // Ausgegebene Zahlen mitzählen
12 } else {
13 return 0; // Anwendung beenden
14 }
15
16 if (i <= benutzer) { // Benutzereingabe erreicht?
17 std::cout << "2\n"; // Ausgabe der Zahl 2
18 ++i; // Ausgegebene Zahlen mitzählen
19 } else {
20 return 0; // Anwendung beenden
21 }
22
23 // ...
24
25 if (i <= benutzer) { // Benutzereingabe erreicht?
26 std::cout << "4294967295\n"; // Ausgabe der Zahl 4294967295
27 ++i; // Ausgegebene Zahlen mitzählen
28 } else {
29 return 0; // Anwendung beenden
30 }
31
32 // Wenn Ihr Compiler diese Stelle erreichen soll, brauchen Sie einen leistungsstarken
33 // Rechner und ein robustes Betriebssystem.
34 // Um dieses Problem zu lösen, setzen Sie sich einfach in die nächste Zeitmaschine
35 // und bestellen Sie sich einen Rechner aus dem Jahr 2020!
36}
Insgesamt würden sich über 17,1 Milliarden Zeilen Code ergeben.
An diesem Punkt kommen Schleifen ins Spiel. Bis jetzt wurden alle Programme einfach der Reihe nach abgearbeitet und zwischendurch wurde eventuell mal verschiedenen Zweigen gefolgt. Mit einer Schleife können Sie erreichen, dass ein Programmteil mehrfach abgearbeitet wird. C++ stellt drei Schleifenkonstrukte zur Verfügung. Die kopfgesteuerte
while
-Schleife, die fußgesteuerte
do-while
-Schleife und die (ebenfalls kopfgesteuerte)
for
-Schleife. Was kopf- und fußgesteuerte Schleife bedeutet erfahren Sie in Kürze. Vielleicht wissen Sie es aber auch schon aus dem Kapitel „Grundlegende Elemente“, aus dem Abschnitt „Für Programmieranfänger“.
Die while
-Schleife[Bearbeiten]
while
Eine
while
-Schleife hat die folgende allgemeine Form:
Natürlich können Sie, wie bei den Verzweigungen auch, hier wieder mehrere Anweisungen zu einem Anweisungsblock zusammenfassen, da diese als eine Anweisung gilt:
Solange die Bedingung erfüllt ist, wird die Anweisung oder der Anweisungsblock ausgeführt. Da es sich hier um eine kopfgesteuerte Schleife handelt, wird erst die Bedingung ausgewertet. Ist diese erfüllt, so wird dann die Anweisung ausgeführt. Ist die nächste Überprüfung der Bedingung positiv, so wird der Schleifenrumpf erneut ausgeführt und so fort. Ist die Bedingung nicht erfüllt, wird der Schleifeninhalt übersprungen und mit dem Quelltext nach der Schleife fortgesetzt. Es kann daher vorkommen, dass der Schleifenrumpf gar nicht ausgeführt wird - wenn die Bedingung schon zu Beginn nicht erfüllt wird.
Was eine fußgesteuerte Schleife macht, erfahren Sie unter der Überschrift
do-while
. Eine Gegenüberstellung der beiden Schleifen gibt es in der Zusammenfassung dieses Kapitels.
Wie Bedingungen ausgewertet werden, können Sie im Kapitel „Verzweigungen“ nachlesen - das vorherige Kapitel.
Beachten Sie, dass Schleifen so lange ausgeführt werden, bis die Bedingung nicht mehr erfüllt ist. Wenn Sie also nicht innerhalb der Schleife dafür sorgen, dass die Bedingung irgendwann nicht mehr erfüllt ist, dann haben Sie eine sogenannte Endlosschleife. Das heißt, der Schleifenrumpf (so nennt man die Anweisung oder den Anweisungsblock einer Schleife) wird immer wieder ausgeführt. Das Programm kann nur durch Abbrechen beendet werden. In Kombination mit einem Schlüsselwort, das Sie in Kürze kennen lernen werden, kann eine solche Endlosschleife durchaus gewollt und sinnvoll sein (der Befehl kann die Schleife abbrechen), aber in der Regel entsteht so etwas versehentlich. Wenn Ihr Programm also mal „abgestürzt“ ist, im Sinne von „Es reagiert nicht mehr! Wie schrecklich!“, dann haben Sie vermutlich irgendwo eine Endlosschleife eingebaut.
Nun aber zurück zu unserer Schleifenaufgabe mit 17,1 Milliarden Zeilen Code. Da Ihr Compiler immer noch nicht damit fertig ist, die oben vorgestellte Lösung zu übersetzen, versuchen wir jetzt mal das ganze mit einer
while
-Schleife zu lösen.
1#include <iostream>
2
3int main() {
4 unsigned i = 1; // 2 Variablen anlegen
5 unsigned benutzer = 0;
6
7 std::cin >> benutzer; // Benutzer gibt Zahl ein
8
9 while (i <= benutzer) { // Benutzereingabe erreicht?
10 std::cout << i << std::endl; // Ausgabe von i und einem Zeilenumbruch
11 i++; // i um eins erhöhen (-> ausgegebene Zahlen mitzählen)
12 }
13}
Nun ja, das sieht dem Programm von oben doch irgendwie ähnlich, nur die knapp 4,3 Milliarden
if
-Anweisungen sind weggefallen und haben einer vom Aufbau fast identischen
while
-Schleife Platz gemacht. Nun haben wir natürlich ein Problem: Ihr Superrechner aus dem Jahr 2020 ist immer noch mit der Übersetzung der ersten Programmversion beschäftigt und wird es wohl auch noch bis zu seiner Erfindung bleiben. Aber zum Glück haben Sie ja noch einen alten Rechner von 1980. Also versuchen Sie das Programm auf diesem zu übersetzen und auszuführen. Tatsächlich. Es funktioniert. Erstaunlich, dass so eine alte Kiste einen Rechner überholt, den Sie sich extra aus der Zukunft haben liefern lassen, um die maximale Leistungsstärke zu bekommen.
Wenn Sie Schwierigkeiten haben das Beispiel nachzuvollziehen, dann sehen Sie sich noch mal die Schleifen-Version für die Zahlen von 1 bis 10 an.
So sieht das Ganze schon viel kompakter und vielleicht auch übersichtlicher aus als die Version von ganz oben. Die Ausgabe dagegen ist völlig identisch. Hier wird
i
am Anfang auf eins gesetzt. Somit ist die Bedingung (eins ist kleiner oder gleich zehn) erfüllt. Damit wird der Schleifenrumpf ausgeführt. „1“ wird ausgegeben. Es folgt ein Zeilenumbruch. Danach wird der Wert von
i
um eins erhöht. Danach wird wieder geprüft, ob die Bedingung erfüllt ist. Da
i
jetzt zwei ist, lautet die Bedingung „zwei ist kleiner oder gleich zehn“, da dies eine wahre Aussage ist, wird wieder der Schleifenrumpf ausgeführt. Das wiederholt sich bis
i
Schließlich den Wert elf hat. Die Bedingung lautet dann „elf ist kleiner oder gleich zehn“, diese Aussage ist zweifellos falsch. Daher wird der Schleifenrumpf nun übersprungen und mit dem Code dahinter weitergemacht. Da in unserem Beispiel dort aber kein Code mehr folgt, wird das Programm beendet.
Zusammengefasst:
- Quelltext vor der Schleife
- Schleifen Bedingung
- Erfüllt:
- Schleifenrumpf
- weiter mit 2
- Nicht erfüllt:
- weiter mit 3
- Erfüllt:
- Quelltext nach der Schleife
Die do-while
-Schleife[Bearbeiten]
do-while
Wie versprochen lüften wir nun das Geheimnis um die fußgesteuerten Schleifen.
do-while
ist eine fußgesteuerte Schleife, das heißt, als erstes wird der Schleifenrumpf ausgeführt, danach die Bedingung überprüft und dann abhängig von der Bedingung, wieder der Rumpf ausgeführt (Bedingung erfüllt) oder mit dem Quelltext nach der Schleife fortgesetzt (Bedingung nicht erfüllt). Eine fußgesteuerte Schleife zeichnet sich also dadurch aus, dass der Schleifenrumpf mindestens ein mal ausgeführt wird.
Bei dieser Schleife finden wir die obige Syntax nur selten, da der Schleifenrumpf hier zwischen den beiden Schlüsselwörtern
do
und
while
steht, wird fast immer ein Anweisungsblock benutzt, auch wenn nur eine Anweisung vorhanden ist. Für Ihren Compiler spielt das natürlich keine Rolle, aber für einen Menschen der den Quelltext liest, ist es übersichtlicher.
Unser anfängliches Riesenprogramm sieht mit einer
do-while
Schleife so aus:
1#include <iostream>
2
3int main() {
4 unsigned i = 1; // 2 Variablen anlegen
5 unsigned benutzer = 0;
6
7 std::cin >> benutzer; // Benutzer gibt Zahl ein
8
9 do { // Schleifenanfang
10 std::cout << i << std::endl; // Ausgabe von i
11 ++i; // Ausgegebene Zahlen mitzählen
12 } while (i <= benutzer); // Benutzereingabe erreicht?
13}
Sie werden feststellen, dass die Ausgabe dieses Programms mit der Ausgabe in der
while
-Schleifen Version übereinstimmt. Den Unterschied bemerken Sie, wenn Sie 0 eingeben: Während die
while
-Version keine Ausgabe macht, gibt diese
do-while
-Version „1“ und einen Zeilenumbruch aus, denn der Schleifenrumpf wird immer erst einmal ausgeführt, erst danach wird die Bedingung überprüft und entschieden ob er noch einmal ausgeführt werden muss.
Zusammengefasst:
- Quelltext vor der Schleife
- Schleifenrumpf
- Schleifen Bedingung
- Erfüllt: weiter mit 2
- Nicht erfüllt: weiter mit 4
- Quelltext nach der Schleife
Die for
-Schleife[Bearbeiten]
for
Die
for
-Schleife ist etwas komplexer als die vorherigen beiden Schleifen. Sie gliedert sich in Teile:
for(«Initialisierungsteil»; «Bedingungsteil»; «Anweisungsteil») «Schleifenrumpf»
Der Schleifenrumpf kann wie immer eine einzelne Anweisung oder ein {}-Anweisungsblock sein. Der Bedingungsteil verhält sich genau wie bei der
while
- und der
do-while
-Schleife oder sagen wir fast genau so, denn einen kleinen aber feinen Unterschied gibt es doch. Während
while
und
do-while
immer eine Bedingung erwarten, muss bei einer
for
-Schleife nicht unbedingt eine Bedingung angegeben werden. Wenn keine Bedingung angegeben ist, wird einfach angenommen, dass die Bedingung immer erfüllt ist, Sie erhalten eine Endlosschleife. Wie das sinnvoll eingesetzt wird erfahren Sie in Kürze. Im Anweisungsteil können Sie eine beliebige Anweisung ausführen, dieser Teil wird oft verwendet, um Variablen bei jedem Schleifendurchlauf hoch oder runter zu zählen. Im nächsten Beispiel wird dies auch demonstriert. Es ist auch möglich, mehrere solcher „hoch- oder runter-Zähl“-Anweisungen durch Komma getrennt anzugeben, das wird im nächstem Beispiel aber nicht gemacht. Der Anweisungsteil wird übrigens direkt nach dem Schleifenrumpf und vor dem nächsten Bedingungstest ausgeführt. Der Initialisierungsteil ist dem Anweisungsteil dahingehend ähnlich, als dass auch hier eine beliebige Anweisung ausgeführt werden kann. Zusätzlich ist es hier aber noch möglich Variablen eines Datentyps anzulegen. Sie können also problemlos 2
int
-Variablen anlegen, aber nicht eine
int
- und eine
char
-Variable. Der Initialisierungsteil wird nur einmal am Beginn der Schleife ausgeführt.
for
while
Jetzt sollten Sie sich jedoch merken, dass Sie eine solche Variable nur innerhalb der Schleife verwenden können, also nicht mehr nach ihrem Verlassen.
1#include <iostream>
2
3int main() {
4 unsigned benutzer = 0; // Variablen für Benutzereingabe
5
6 std::cin >> benutzer; // Benutzer gibt Zahl ein
7
8 for(unsigned i = 1; i <= benutzer; ++i) // for-Schleife
9 std::cout << i << std::endl; // Ausgabe von i
10}
Zusammengefasst:
- Quelltext vor der Schleife
- Initialisierungsteil der Schleife
- Schleifen Bedingung
- Erfüllt:
a) Schleifenrumpf
b) Anweisungsteil
c) weiter mit 3 - Nicht erfüllt: weiter mit 4
- Erfüllt:
- Quelltext nach der Schleife
for
for
Zu erkennen ist sie daran, das es keine zwei Semikolons zwischen den Klammern gibt, sondern einen Doppelpunkt.
Die break
-Anweisung[Bearbeiten]
break
Jetzt ist es an der Zeit, das Geheimnis um die sinnvolle Verwendung von Endlosschleifen zu enthüllen. Die
break
-Anweisung wird innerhalb von Schleifen verwendet, um die Schleife sofort zu beenden. Der Quelltext wird dann ganz normal nach der Schleife fortgesetzt.
break
können Sie in jeder der drei Schleifen verwenden. Das folgende Beispiel demonstriert den Einsatz von
break
, anhand unseres Lieblingsprogramms in diesem Kapitel.
1#include <iostream>
2
3int main() {
4 unsigned i = 1; // 2 Variablen anlegen
5 unsigned benutzer = 0;
6
7 std::cin >> benutzer; // Benutzer gibt Zahl ein
8
9 for(;;){ // Endlos-for-Schleife
10 std::cout << i << "\n"; // Ausgabe von i
11 ++i; // Variable erhöhen
12 if(i > benutzer) break; // Abbrechen, wenn Bedingung erfüllt
13 }
14}
Sie können aus jeder Schleife eine Endlosschleife machen, hier wurde die
for
-Schleife gewählt um zu zeigen wie Sie ohne Bedingung aussieht. Bei einer
while
- oder
do-while
-Schleife könnten Sie beispielsweise
true
als Bedingung angeben. Die for-Schleife legt jetzt übrigens das gleiche Verhalten an den Tag, wie eine
do-while
-Schleife. Sie können auch problemlos nur einen oder zwei Teile des Schleifenkopfes bei
for
-Schleife übergeben, wichtig ist nur, dass Sie die beiden Semikolons immer angeben, da Sie dem Compiler mitteilen, was welcher Teil des Schleifenkopfes ist. Nun wissen Sie wieder etwas mehr über
for
, dabei sollte es unter dieser Überschrift doch eigentlich um
break
gehen. Wie Sie aber sehen, hängt letztlich alles mit allem zusammen und so ist es oft schwer eine richtige Abgrenzung zu schaffen.
Die continue
-Anweisung[Bearbeiten]
continue
Das zweite wichtige Schlüsselwort für Schleifen ist
continue
. Es wird genau so verwendet wie
break
, bricht die Schleife allerdings nicht völlig ab, sondern setzt die Codeausführung am Ende des Schleifenrumpfes fort. Für
while
und
do-while
bedeutet das beim Bedingungstest, für die
for
-Schleife beim Anweisungsteil. Mit
continue
können Sie also den Rest des aktuellen Schleifendurchlaufs überspringen. Wir sehen uns das wieder anhand des Beispiels an.
1#include <iostream>
2
3int main() {
4 unsigned benutzer = 0; // Variablen für Benutzereingabe
5
6 std::cin >> benutzer; // Benutzer gibt Zahl ein
7
8 for(unsigned i = 1; i <= benutzer; ++i) { // for-Schleife
9 if (i % 2 == 0) continue; // alle geraden Zahlen überspringen
10 std::cout << i << std::endl; // Ausgabe von i
11 }
12}
Hier werden nur ungrade Zahlen ausgegeben, da der
%
-Operator (liefert den Rest einer Ganzzahligen Division) bei allen geraden Zahlen, (teilt man durch 2 ist als Rest ja nur 0 oder 1 möglich,) 0 zurückliefert und somit
continue
ausgeführt wird.
Natürlich könnten Sie das auch noch auf eine andere Weise realisieren. Aber es gibt ja beim Programmieren viele Wege, die nach Rom führen, wie in diesem Kapitel anhand der verschieden Schleifen schon bewiesen wurde. Leider gibt es aber noch mehr Wege, die auf direktem Wege an Rom vorbeiführen... Aber hier ist für das Beispiel von eben noch ein Pfad, der sicher nach Rom führt:
1#include <iostream>
2
3int main() {
4 unsigned int benutzer = 0; // Variablen für Benutzereingabe
5
6 std::cin >> benutzer; // Benutzer gibt Zahl ein
7
8 for(unsigned i = 1; i <= benutzer; i += 2) // for-Schleife mit 2er Schritten
9 std::cout << i << std::endl; // Ausgabe von i
10}
Strukturierte Programmierung[Bearbeiten]
Die Anweisungen 'break' und 'continue' gelten als „unsauber“, sie erschweren fast immer das Verständnis des Programms. Die Verwendung von 'break' ist oft ein Zeichen dafür, dass die Schleifenbedingung unvollständig ist - und man daher nicht einzig aus der Schleifenbedingung ersehen kann, wie lange die Schleife läuft. Die 'continue'-Anweisung kann meist durch eine 'if'-Anweisung ersetzt werden - oder ebenfalls durch eine exaktere Schleifenbedingung. In als „gut“ erachteten Programmen kommen 'break' und 'continue' möglichst gar nicht vor.
Kapitelanhang[Bearbeiten]
Im Anhang zu diesem Kapitel finden Sie:
- Aufgaben und zugehörige Musterlösungen.

- Am Anfang gibt der Spieler einen Zahlenbereich ein. (Zum Beispiel: 1-100)
- Der Spieler muss sich innerhalb dieses Bereiches eine Zahl merken (eingegebene Grenzzahlen sind nicht zulässig).
- Das Programm soll dann die Zahl erraten. Der Benutzer teilt dem Programm mit, ob die Zahl an die er denkt kleiner, größer oder gleich der vom Programm geratenen Zahl ist. Die kann zum Beispiel über die Eingabe von ,
<
und>
erfolgen.=
Es gibt natürlich viele Wege dieses Problem zu lösen und Sie sehen ja ob Ihr Programm funktioniert oder nicht. Hier wird nur eine Musterlösung vorgestellt, falls Sie überhaupt nicht zurechtkommen oder sich einfach dafür interessieren wie der Autor an das Problem herangegangen ist.
1#include <iostream>
2
3int main() {
4 int min, max; // Variablen für den möglichen Zahlenbereich
5 int zahl; // Zahl die der Rechner vermutet
6
7 std::cout << "Wo fängt die Zahlenreihe an?: "; // Zahlenbereich abfragen
8 std::cin >> min; // Benutzereingabe einlesen
9
10 std::cout << "Wo hört die Zahlenreihe auf?: "; // Zahlenbereich abfragen
11 std::cin >> max; // Benutzereingabe einlesen
12
13 for (char eingabe = '0'; eingabe != '=';) { // Abbrechen wenn eingabe '=' ist
14 zahl = min + (max - min) / 2; // Mittlere Zahl berechnen
15 std::cout << "Denken Sie an " << zahl << "? "; // Vermutung ausgeben
16 std::cin >> eingabe; // Antwort einlesen
17
18 if (eingabe == '<') // Ist die Zahl kleiner?
19 max = zahl; // Setzte max auf den zu großen Wert zahl
20 else if (eingabe == '>') // Ist die Zahl größer?
21 min = zahl; // Setzte min auf den zu kleinen Wert zahl
22 else if (eingabe != '=') // Ist Eingabe auch kein Gleichheitszeichen
23 std::cout << "Sie haben ein unzulässiges Zeichen eingegeben!\n"; // Fehlerhafte Eingabe melden
24
25 if (min+1 >= max) { // Keine Zahl mehr im gültigen Bereich
26 std::cout << "Sie sind ein Lügner!\n"; // Das Programm ist äußert entsetzt
27 break; // Schleife wird abgebrochen
28 }
29 }
30
31 std::cout << "Die von Ihnen gemerkte Zahl ist " << zahl << "!" << std::endl; // Ausgabe der erratenen Zahl
32}
1Wo fängt die Zahlenreihe an?: 0
2Wo hört die Zahlenreihe auf?: 100
3Denken Sie an 50? <
4Denken Sie an 25? >
5Denken Sie an 37? <
6Denken Sie an 31? >
7Denken Sie an 34? <
8Denken Sie an 32? >
9Denken Sie an 33? =
10Die von Ihnen gemerkte Zahl ist 33!
Dies ist eine Beispielausgabe, alles nach einem Fragezeichen ist eine Benutzereingabe.