Wiederholen ohne Anzahl
Eine for-Schleife ist das richtige Werkzeug, wenn du weißt - oder berechnen kannst -, wie viele Durchläufe du brauchst. Viel Arbeit kommt ohne Anzahl. Jede Zeile einer Datei lesen, bis sie endet. Weiter nach einem Passwort fragen, bis es stimmt. Eine Zahl halbieren, bis sie 1 erreicht. Hier lautet die Frage nicht „wie oft", sondern „bis wann", und genau das drückt while aus.
Niemand hat vorab „sieben" berechnet. Die Schleife macht einfach weiter, solange die Bedingung gilt.
Syntax und die Bedingung-zuerst-Regel
while (condition) {
// Rumpf - läuft, solange die Bedingung ungleich null ist
}
Die Bedingung wird vor jedem Durchlauf geprüft, auch vor dem ersten. Drei Konsequenzen:
- Eine Schleife, deren Bedingung von Anfang an falsch ist, läuft null Mal.
while (0) { ... }führt den Rumpf nie aus. - Was die Bedingung prüft, muss vor der Schleife initialisiert sein, sonst prüfst du einen Müllwert.
- Etwas im Rumpf muss die Bedingung irgendwann falsch machen, sonst endet die Schleife nie.
Wie bei if sind die Klammern Pflicht, und dahinter steht kein Semikolon. Dieses verirrte Semikolon ist die Schleifenfassung desselben Bugs:
/* BUG: die leere Anweisung ist der Rumpf - das dreht sich ewig */
while (i < 10);
{
printf("%d\n", i);
i++;
}
i wird nie erhöht, die Bedingung ändert sich also nie. gcc -Wall beanstandet den leeren Rumpf.
Cs Wahrheitsregel gilt hier genauso wie in if/else: Die Schleife läuft weiter, solange der Ausdruck ungleich null ist, und stoppt, wenn er null ist.
Die Standardform: initialisieren, prüfen, aktualisieren
Fast jede korrekte while-Schleife hat drei Teile, verteilt auf drei Stellen:
Das sind dieselben drei Teile, die ein for-Kopf hält, nur nicht in einer Zeile gesammelt. Und das ist die ehrliche Antwort auf „for oder while?": Wenn alle drei Teile denselben Zähler betreffen, setzt ein for sie dorthin, wo ein Leser sie zusammen prüfen kann. Ist die Aktualisierung über einen langen Rumpf verstreut, oder betrifft die Bedingung etwas anderes als eine Anzahl, ist while die klarere Form.
Teil 3 zu vergessen ist der while-Schleifen-Bug. Wenn eine Schleife hängt, ist das Erste, was man prüft, ob der Rumpf tatsächlich ändert, was die Bedingung liest.
Wächterschleifen
Ein Wächter ist ein besonderer Wert, der „stopp" bedeutet. Zahlen summieren, bis der Benutzer 0 eingibt:
Die 99 hinter der 0 wird nie erreicht - der Wächter hat die Schleife beendet. Beachte, dass die Anzahl der Zahlen nie vorab bekannt ist, und genau deshalb ist das ein while.
C-Zeichenketten funktionieren genauso: Sie enden mit einem '\0'-Wächter, eine davon zu durchlaufen ist also ein Lehrbuch-while:
Eingaben bis zum Dateiende lesen
Das häufigste while in der Praxis liest Eingaben, bis keine mehr da sind. Die Regel lautet, den Rückgabewert der Eingabefunktion zu prüfen, nicht den gespeicherten Wert.
getchar liefert das nächste Zeichen oder die Konstante EOF, wenn die Eingabe ausgeht:
Zwei wichtige Einzelheiten. c ist ein int, weil EOF ein negativer Wert ist, der auf vielen Systemen nicht in ein char passt - es als char zu deklarieren kann dazu führen, dass der Vergleich nie gelingt. Und die Zuweisung sitzt in der Bedingung: (c = getchar()) != EOF liest ein Zeichen, speichert es und vergleicht in einem Schritt. Die inneren Klammern sind Pflicht, denn != bindet stärker als =; ohne sie würdest du das Ergebnis des Vergleichs in c speichern. Das ist eine der wenigen Stellen, an denen eine Zuweisung in einer Bedingung idiomatisch statt ein Bug ist.
Dieselbe Form funktioniert mit scanf, dessen Rückgabewert die Anzahl der erfolgreich umgewandelten Elemente ist:
int n;
while (scanf("%d", &n) == 1) {
/* n hält eine gültige Zahl */
}
Die Schleife endet am Dateiende und bei der ersten Eingabe, die keine Zahl ist, und das ist meist das, was du willst.
Was du nicht schreiben solltest, ist while (!feof(f)). feof meldet, ob ein Lesezugriff bereits fehlgeschlagen ist, es ist also noch falsch, wenn du auf dem letzten Element sitzt, der Schleifenrumpf läuft ein weiteres Mal und der Lesezugriff scheitert - und verarbeitet veraltete Daten. Prüfe den Lesezugriff selbst.
while (1) und break
Manchmal ist die Abbruchbedingung erst mitten im Rumpf bekannt - nach dem Lesen, aber vor der Verarbeitung. Die idiomatische Antwort ist eine absichtlich endlose Schleife mit einem ausdrücklichen Ausstieg:
while (1) ist immer wahr, der einzige Ausweg ist also break (oder return oder exit). Du wirst for (;;) für denselben Zweck sehen; beides übersetzt sich zum selben, und was eine Codebasis bevorzugt, ist Stilsache.
Nimm diese Form, wenn die Abbruchprüfung wirklich mitten in den Rumpf gehört. Passt die Prüfung natürlich an den Anfang, setze sie in die Bedingung, wo ein Leser sie sieht - ein while (1), dessen einziges break die erste Anweisung ist, ist nur eine verkleidete Bedingung.
Zwischen for und while umwandeln
Jede for-Schleife lässt sich als while-Schleife schreiben und umgekehrt. Die Abbildung ist mechanisch:
/* for */ /* gleichwertiges while */
for (init; cond; update) { init;
body; while (cond) {
} body;
update;
}
Diese beiden geben also dasselbe aus:
Die eine Stelle, an der die Gleichwertigkeit leckt, ist continue. In einer for-Schleife springt continue zur Aktualisierung im Kopf, der Zähler rückt also weiter vor. In der while-Fassung springt continue direkt zurück zur Bedingung und überspringt das i++ am Ende des Rumpfes - eine sofortige Endlosschleife:
/* BUG: continue überspringt i++, die Schleife dreht sich bei der ersten geraden Zahl ewig */
int i = 0;
while (i < 10) {
if (i % 2 == 0) {
continue;
}
printf("%d ", i);
i++;
}
Benutzt eine while-Schleife ein continue, stelle sicher, dass die Aktualisierung davor passiert - oder nimm ein for, wo der Kopf es garantiert.
Häufige Fallstricke
- Nichts aktualisiert die Bedingung. Der Rumpf muss ändern, was die Bedingung liest. Wenn ein Programm hängt, sieh zuerst hier nach.
- Ein Semikolon hinter dem Kopf.
while (bed);macht die leere Anweisung zum Rumpf. - Eine nie initialisierte Variable prüfen. Die Bedingung läuft vor dem Rumpf, der gelesene Wert muss also schon existieren.
charstattintbeigetchar.EOFpasst nicht in einchar; der Vergleich kann dann nie wahr werden, oder ein legitimes Byte wird fürEOFgehalten.while (!feof(f)). Läuft einmal zu oft. Prüfe den Lesezugriff.- Annehmen, der Rumpf laufe mindestens einmal. Tut er nicht. Wenn du einen garantierten Durchlauf brauchst - ein Menü ausgeben, bevor du die Wahl prüfst -, ist das eine do-while-Schleife.
Häufig gestellte Fragen
Wie schreibt man eine while-Schleife in C?
while (bedingung) { rumpf }. Die Bedingung wird vor jedem Durchlauf geprüft, der Rumpf läuft also nur, solange die Bedingung ungleich null ist, und gar nicht, wenn sie von Anfang an falsch ist. Etwas im Rumpf muss die Bedingung irgendwann ändern, sonst endet die Schleife nie.
Was ist der Unterschied zwischen for und while in C?
Sie sind gleich mächtig - jedes for lässt sich als while schreiben. Nimm for, wenn ein Zähler, seine Grenze und seine Schrittweite zusammen in einen Kopf gehören, und while, wenn die Abbruchbedingung keine Anzahl ist: bis zum Dateiende lesen, bis zum Erfolg wiederholen oder schleifen, bis ein Flag umkippt.
Wie lese ich in C Eingaben bis zum Dateiende?
Prüfe den Rückgabewert der Eingabefunktion, nicht den gelesenen Wert: while (scanf("%d", &n) == 1) { ... } stoppt am Dateiende oder bei der ersten nicht-numerischen Eingabe, und while ((c = getchar()) != EOF) { ... } liest Zeichen für Zeichen. Schleife nie über while (!feof(f)) - das läuft einmal zu oft über das Ende hinaus.
Warum ist meine while-Schleife in C endlos?
Meist, weil nichts im Rumpf den Wert ändert, den die Bedingung prüft, oder weil ein verirrtes Semikolon hinter dem Kopf (while (i < 10);) die leere Anweisung zum Rumpf gemacht hat. Beides kompiliert sauber, prüfe also, ob der Rumpf die Variable der Bedingung wirklich aktualisiert.