1. Einführung in Schleifen
Wenn man gerade anfängt zu programmieren, erscheint es logisch: „Also ich muss 1, 2, 3... ausgeben, dann schreibe ich drei cout“. Das funktioniert genau so lange, bis man 1…100 ausgeben oder eine Aktion so lange wiederholen muss, bis die Eingabe korrekt ist. An diesem Punkt wird Copy-Paste zur Katastrophe: den Fehler müssen Sie an 100 Stellen beheben, und die Augen werden anfangen, den Monitor zu hassen.
Eine Schleife löst dieses Problem einfach: statt „von Hand wiederholen“ beschreibt man die Wiederholregel. Ganz grob ist eine Schleife ein Vertrag mit dem Computer: „Ich gebe dir eine Bedingung und einen Rumpf, und du wiederholst ordentlich, solange die Bedingung wahr ist“. Und der Computer so: „Ah, endlich eine normale Aufgabe“.
Syntax der while-Schleife und Ausführungsreihenfolge
Die while-Schleife sollte man auf der Ebene „wie sie tatsächlich ausgeführt wird“ verstehen, sonst schreibt man Schleifen, die entweder gar nicht ausgeführt werden oder ewig laufen. Die gute Nachricht: die Regel ist sehr einfach.
Form der Schleife:
while (Bedingung) {
// Schleifenrumpf
}
Die Idee: vor jeder Ausführung des Rumpfs wird die Bedingung ausgewertet. Ist sie wahr (true), wird der Rumpf ausgeführt. Ist sie falsch (false), endet die Schleife (oder sie beginnt überhaupt nicht).
Mini‑Beispiel „Countdown“:
#include <iostream>
int main() {
int x = 3;
while (x > 0) {
std::cout << x << '\n'; // 3, dann 2, dann 1
x = x - 1;
}
std::cout << "Go!\n"; // Go!
}
Hier passiert etwas sehr „ehrliches“: solange x > 0, drucken wir x und verringern es. Sobald x 0 wird, ist die Bedingung falsch und die Schleife stoppt.
Achten Sie auf einen wichtigen Gedanken: while kann null Mal ausgeführt werden. Zum Beispiel:
#include <iostream>
int main() {
int x = 0;
while (x > 0) {
std::cout << "Inside\n";
}
std::cout << "Outside\n"; // Outside
}
Das ist kein Bug und keine „Eigenheit von C++“. Das ist normale Logik: ist die Bedingung sofort falsch, gibt es nichts auszuführen.
2. Die drei Teile einer guten while-Schleife: Initialisierung, Bedingung, Schritt
Die meisten „normalen“ (also terminierenden) while-Schleifen lassen sich gedanklich in drei Teile gliedern. Das hilft sehr beim Finden von Fehlern.
Vor der Schleife steht üblicherweise die Initialisierung: wir setzen Anfangswerte. Im Kopf der while steht die Fortsetzungsbedingung. Und im Rumpf sollte ein Schritt stehen, der uns der Situation nähert, dass die Bedingung falsch wird.
Es ist praktisch, so eine Tabelle im Kopf zu haben:
| Teil | Wo | Beispiel | Warum |
|---|---|---|---|
| Initialisierung | vor der Schleife | |
Start setzen |
| Bedingung | in while (...) | |
entscheiden, ob fortfahren |
| Schritt | im Rumpf | |
sich dem Ende nähern |
Schauen wir uns die klassische Aufgabe an: die Summe der Zahlen von 1 bis n berechnen.
#include <iostream>
int main() {
int n = 0;
std::cin >> n;
int i = 1; // Initialisierung
int sum = 0; // Initialisierung
while (i <= n) { // Fortsetzungsbedingung
sum = sum + i;
i = i + 1; // Schritt zur Beendigung
}
std::cout << sum << '\n';
}
Hier lässt sich die Idee leicht „lesen“: solange i nicht bei n angekommen ist, addieren wir i zur Summe und erhöhen i. Der Schritt garantiert, dass irgendwann i den Wert n + 1 erreicht, die Bedingung i <= n falsch wird und die Schleife stoppt.
3. Schleifeninvariant: das innere „Gelöbnis“
Das Wort „Invariant“ klingt wie ein Bossname aus einem RPG, aber praktisch ist es sehr einfach. Ein Invariant ist eine Aussage, die bei jeder Iteration wahr bleibt (bei jeder „Runde“ der Schleife). Er dient nicht der Mathematik, sondern der Selbstprüfung: „Mache ich eigentlich das, was ich denke?“
Nehmen wir die Summe 1..n. Ein nützlicher Invariant in dieser Schleife lautet: „Vor jeder Iteration enthält sum die Summe der Zahlen von 1 bis i - 1“. Dieser Satz wirkt langweilig, schützt Sie aber sofort vor typischen Fehlern: wenn Sie aus Versehen mit i = 0 beginnen oder vergessen, i = i + 1 zu schreiben, wird das Invariant nicht mehr wahr sein (oder Sie können nicht erklären, warum es wahr ist) – also ist die Logik gebrochen.
Schauen wir uns das für ein kleines n = 3 und überprüfen das Invariant „manuell“:
- vor dem Eintritt in die Schleife: i = 1, sum = 0. „Summe von 1 bis 0“ ist 0. Invariant gilt.
- nach der ersten Iteration: 1 addiert, jetzt sum = 1, i = 2. Jetzt ist „Summe von 1 bis 1“ = 1. Gültig.
- nach der zweiten: sum = 3, i = 3. „Summe von 1 bis 2“ = 3. Gültig.
- nach der dritten: sum = 6, i = 4. „Summe von 1 bis 3“ = 6. Gültig. Dann wird i <= n zu 4 <= 3 → false und die Schleife stoppt.
Das Invariant ist Ihr innerer Kompass. Wenn der Kompass zu lügen beginnt, sind Sie nicht im Wald, sondern in einer Schleife ohne Schritt.
4. Nützliche while-Muster an kurzen Beispielen
Jetzt üben wir an Aufgaben, die ständig auftauchen. Ziel ist nicht „die Aufgabe lösen“, sondern das wiederkehrende Muster zu erkennen: Initialisierung, Bedingung, Schritt.
Anzahl der Ziffern in einer Zahl
Manchmal ist der „Schritt zur Beendigung“ nicht die Erhöhung eines Zählers, sondern die Verkleinerung des zu bearbeitenden Objekts.
#include <iostream>
int main() {
int n = 0;
std::cin >> n;
int digits = 0;
while (n > 0) {
digits = digits + 1;
n = n / 10; // Schritt: Zahl verkleinern
}
std::cout << digits << '\n';
}
Wichtig ist hier, dass n langsam kleiner wird: 1234 → 123 → 12 → 1 → 0. Und dann endet die Schleife.
Wenn Sie 0 eingeben, wird die Schleife kein einziges Mal ausgeführt und 0 ausgegeben. Das ist logisch; manchmal möchte man jedoch, dass die Zahl 0 als eine Ziffer gezählt wird. Das ist ein Beispiel dafür, wie Aufgabenanforderungen die Schleifenlogik beeinflussen — heute gehen wir nicht tiefer, nur zur Kenntnis nehmen.
Finden des ersten Teilers ohne break
Wir haben break noch nicht behandelt (kommt später), daher machen wir ein Beispiel ohne vorzeitigen Abbruch: mittels eines „Ergebnisflags“.
#include <iostream>
int main() {
int n = 0;
std::cin >> n;
int d = 2;
int first_divisor = 0;
while (d <= n && first_divisor == 0) {
if (n % d == 0) {
first_divisor = d;
}
d = d + 1;
}
std::cout << first_divisor << '\n';
}
Hier ist die Fortsetzungsbedingung zusammengesetzt: „wir gehen die möglichen Teiler durch, solange wir n noch nicht erreicht haben und noch keinen Teiler gefunden haben“. Das ist ein gutes Beispiel dafür, dass eine while-Schleife nicht unbedingt „genau N Mal“ laufen muss: sie kann stoppen, sobald das Ziel erreicht ist.
Einlesen von Zahlen bis zum Stoppwert
Das ist ein sehr praxisnahes Szenario: wir lesen Zahlen ein, bis der Benutzer 0 eingibt (oder -1 oder ein anderes Signal).
#include <iostream>
int main() {
int x = 0;
std::cin >> x;
int sum = 0;
while (x != 0) {
sum = sum + x;
std::cin >> x; // Schritt: nächsten Wert einlesen
}
std::cout << sum << '\n';
}
Hier ist der „Schritt zur Beendigung“ die neue Eingabe, die womöglich 0 wird und die Schleife stoppt. Wichtig: ein Schritt kann die Veränderung einer Variablen durch cin sein.
5. Typische endlose Schleifen: wie sie entstehen und wie man sie „heilt“
Eine endlose Schleife ist nicht immer böse (manchmal beabsichtigt), aber für Anfänger bedeutet Endlosigkeit meist: „ich habe den Schritt vergessen“ oder „die Bedingung ist falsch formuliert“. Also hängt das Programm nicht „weil C++ kompliziert ist“, sondern macht genau das, worum Sie es gebeten haben — nur für immer.
Eine kleine Tatsache aus der Standardisierungswelt: beim C++-Komitee diskutiert man gelegentlich „triviale endlose Schleifen“ als formalen Fall von Programmverhalten.
Wir bleiben praktischer: wir lernen, Endlosschleifen im eigenen Code zu finden, bevor sie Sie finden.
Fehler Nr. 1: die Bedingungsvariable ändert sich nicht
So einfach lässt sich eine Schleife „kaputt machen“:
#include <iostream>
int main() {
int x = 3;
while (x > 0) {
std::cout << x << '\n';
// x = x - 1; // Schritt vergessen
}
}
Die Bedingung x > 0 hängt von x ab, aber x ändert sich nicht. Also ist die Bedingung immer wahr. Ergebnis: endloses Ausgeben von „3“.
Die Heilung ist banal: den Schritt wieder einfügen (x = x - 1;). Eine nützliche Gewohnheit: vor dem Schleifenstart sich laut zu beantworten, „was ändert sich pro Iteration und warum bringt das die Schleife dem Ende näher?“.
Fehler Nr. 2: ein versehentliches ; nach while
Das ist einer der tückischsten Fehler, weil man ihn mit dem Auge kaum sieht. So:
#include <iostream>
int main() {
int x = 3;
while (x > 0); { // <- ACHTUNG: überflüssiges Semikolon
std::cout << x << '\n';
x = x - 1;
}
}
Was passiert? while (x > 0); ist eine Schleife mit leerem Rumpf. Sie wird „dauerkreisen“ und nichts tun. Der Block { ... } danach ist einfach ein gewöhnlicher Block, der einmal ausgeführt wird (nachdem die Schleife – die aber nicht endet, weil x innerhalb nicht geändert wird).
Um das zu vermeiden, schreiben Sie in Lehrbeispielen immer geschweifte Klammern und setzen die öffnende { auf die nächste Zeile, sodass das Semikolon auffälliger ist.
Fehler Nr. 3: der Schritt ist vorhanden, aber der falsche
Manchmal schreibt man ehrlich einen Schritt … aber ändert nicht die Variable, von der die Bedingung abhängt.
#include <iostream>
int main() {
int x = 3;
int y = 0;
while (x > 0) {
y = y + 1; // Schritt ändert y, aber die Bedingung hängt von x ab
std::cout << y << '\n';
}
}
Das ist auch eine Endlosschleife: x ist weiterhin 3.
Hier hilft die einfache Technik: listen Sie die Variablen auf, die in der Bedingung vorkommen (x), und prüfen Sie, dass sie im Rumpf so verändert werden, dass die Bedingung irgendwann falsch wird.
Fehler Nr. 4: Schleife fürs Einlesen ohne erneutes Einlesen
Sehr häufig: man will viele Zahlen einlesen, vergisst aber, das „nächste“ einzulesen.
#include <iostream>
int main() {
int x = 0;
std::cin >> x;
while (x != 0) {
std::cout << x << '\n';
// std::cin >> x; // vergessen, x zu aktualisieren
}
}
Wenn x nicht 0 war, wird die Schleife immer dieselbe Zahl endlos ausgeben. In Einleseschleifen sieht der „Schritt“ fast immer wie ein wiederholtes cin >> ... aus oder eine Veränderung des Wertes, den Sie einlesen.
6. Mini‑Beispiel und Flussdiagramm der while-Schleife
Damit die Beispiele nicht nur einzelne Programme bleiben, vereinbaren wir: wir bauen eine einfache Konsolenanwendung, die Statistiken über eingegebene Zahlen sammelt. Heute fügen wir den Hauptmechanismus hinzu: Wiederholung der Eingabe bis zum Stoppkommando.
Idee: der Benutzer gibt Zahlen ein (z. B. „Punkte pro Runde“), und 0 bedeutet „fertig“. Wir zählen Summe, Anzahl und Maximum. Wir benutzen noch keine Arrays oder Vektoren (die kommen später), daher speichern wir nur Aggregatswerte.
#include <iostream>
int main() {
int x = 0;
std::cin >> x;
int sum = 0;
int count = 0;
int max_value = 0;
while (x != 0) {
sum = sum + x;
count = count + 1;
if (x > max_value) {
max_value = x;
}
std::cin >> x;
}
std::cout << "count=" << count << '\n';
std::cout << "sum=" << sum << '\n';
std::cout << "max=" << max_value << '\n';
}
Wenn Sie 5 2 9 0 eingeben, gibt das Programm etwa Folgendes aus:
count=3
sum=16
max=9
Hier sieht man gut, wie while mit bekannten Konzepten zusammenarbeitet: Variablen, if, Vergleiche, Ein-/Ausgabe. Das ist im Grunde eines der häufigsten „realen“ Einsatzszenarien für Schleifen.
Flussdiagramm: wie while „denkt“
Um die Ausführungsreihenfolge endgültig zu verfestigen, ist es nützlich, sich die while als einfache Schema-Darstellung vorzustellen. Das ist keine Magie, sondern ein sehr vorhersagbarer Automat:
flowchart TD
A[Start] --> B[Bedingung berechnen]
B -->|false| E[Ausgang aus der Schleife]
B -->|true| C[Rumpf ausführen]
C --> D[Zur Überprüfung zurückkehren]
D --> B
Wenn Sie jemals unsicher sind, „warum die Schleife nicht reingeht“ oder „warum sie nicht endet“, kehren Sie zu diesem Bild zurück und fragen: Ist die Bedingung false? Oder ist die Bedingung immer true? Oder fehlt der Schritt?
7. Typische Fehler bei der Arbeit mit while
Fehler Nr. 1: kein Schritt zur Beendigung, und die Bedingung ändert sich niemals.
Das äußert sich meist als „druckt immer dasselbe“ oder „Programm hängt“. Fast immer liegt der Grund darin, dass die Variable aus der Bedingung im Rumpf nicht verändert wird. Heilbar durch Disziplin: vor Schleifenstart laut erklären, welche Zeile den Schritt macht und warum die Bedingung irgendwann falsch wird.
Fehler Nr. 2: überflüssiges Semikolon nach while (Bedingung);.
Dieser Fehler ist besonders fies, weil der Compiler nicht unbedingt meckert: die Syntax ist formal korrekt. Aber die Logik ändert sich radikal: die Schleife wird leer, und der Block danach läuft separat. Die Gewohnheit, den Rumpf in { ... } zu schreiben und die Zeilen gut zu formatieren, reduziert die Chance, diese „unsichtbare Wand“ zu übersehen.
Fehler Nr. 3: falsche Grenze (< statt <= oder umgekehrt).
Bei while zeigt sich das als „eine Iteration mehr/weniger“. Um das zu vermeiden, rechnen Sie gedanklich die ersten zwei Werte und den letzten erwarteten Wert durch. Wenn Sie n mitaufnehmen wollen, sollte die Bedingung i <= n lauten. Wenn nicht, i < n. Die Wahl des Vergleichszeichens ist kein Kosmetikdetail, sondern Sache der Semantik.
Fehler Nr. 4: in Einleseschleifen das Einlesen des Wertes vergessen.
Wenn Sie x vor der Schleife einlesen, aber im Rumpf kein std::cin >> x mehr durchführen, arbeitet die Schleife mit demselben Wert ewig. In Schleifen, die „viele Werte einlesen“, ist der Schritt meistens genau dieses wiederholte Einlesen: ohne ihn bewegt sich die Schleife nicht.
Fehler Nr. 5: der Schritt ändert die falsche Variable.
Manchmal denkt man, „ich ändere ja etwas“, aber die Bedingung hängt von einer anderen Variablen. Abhilfe: unterstreichen Sie die Variablen in der Bedingung while (...) und vergewissern Sie sich, dass mindestens eine davon so geändert wird, dass die Bedingung falsch wird. Wenn keine geändert wird, haben Sie fast sicher eine ewige Maschine (und das Patent dafür wurde noch nicht erteilt).
GO TO FULL VERSION