Zwei Wege, den Ablauf einer Schleife zu ändern
Die Bedingung einer Schleife entscheidet, wann sie endet, aber manchmal taucht die eigentliche Antwort mitten im Rumpf auf. Du durchsuchst ein Array und findest das Element an der dritten Stelle - warum die anderen 997 prüfen? Oder du verarbeitest eine Liste und ein Eintrag ist leer - überspring ihn und mach weiter.
C hat für jeden Fall ein Schlüsselwort:
break- die Schleife jetzt verlassen. Die Ausführung geht bei der ersten Anweisung danach weiter.continue- nur diesen Durchlauf abbrechen. Weiter zum nächsten.
Die erste Schleife gibt 1 2 3 4 aus und stoppt. Die zweite gibt 1 2 3 4 6 7 8 9 10 aus - sie hat einen Wert übersprungen und weitergemacht.
break in einer Schleife
Der kanonische Einsatz ist eine Suche, die stoppt, sobald sie Erfolg hat:
Weil found vor dem break gesetzt wird, kann der Code nach der Schleife zwischen „gefunden" und „Elemente ausgegangen" unterscheiden. Diese Paarung - eine Ergebnisvariable plus ein break - ist das Standard-Suchidiom in C. Der Schleifenzähler selbst ist danach nicht nutzbar, wenn er im Kopf deklariert wurde, denn er verlässt bei der schließenden Klammer seinen Gültigkeitsbereich.
break verlässt immer eine Schleife: das nächstliegende umschließende for, while, do-while oder switch.
continue in einer Schleife
continue ist für Einträge gedacht, die übersprungen werden sollen, statt die Arbeit zu stoppen:
Wohin continue springt, hängt von der Schleife ab:
for -> zum Aktualisierungsteil des Kopfes (der Zähler rückt also weiter vor)
while -> zur Bedingungsprüfung (nichts anderes im Rumpf läuft)
do-while -> zur Bedingungsprüfung am Ende
Dieser Unterschied ist nicht akademisch. In einer for-Schleife kann continue die Erhöhung des Zählers nie überspringen, weil sie im Kopf steht. In einer while-Schleife schon:
/* BUG: continue überspringt i++, das hängt also bei der ersten geraden Zahl */
int i = 0;
while (i < 10) {
if (i % 2 == 0) {
continue;
}
printf("%d ", i);
i++;
}
Verschiebe entweder i++ über das continue oder schreibe es als for-Schleife, wo der Kopf es unmöglich macht, es falsch zu machen.
Ein break im switch ist kein break der Schleife
break beendet die nächstliegende umschließende Schleife oder das switch - was näher ist. Setze ein switch in eine Schleife, und jedes break darin gehört zum switch:
Der Befehl q gibt seine Meldung aus, und die Schleife läuft durch cc weiter. Wenn das Beenden die Schleife hätte beenden sollen, hat dieses break es nicht getan. Die Lösungen sind ein Flag, ein goto oder das Verschieben der Schleife in eine Funktion mit return - alle unten gezeigt.
Aus verschachtelten Schleifen ausbrechen
Ein einzelnes break verlässt eine Ebene. Innerhalb zweier verschachtelter Schleifen beendet es nur die innere, und die äußere beginnt ihren nächsten Durchlauf, als wäre nichts geschehen.
C hat kein break 2 und kein benanntes break. Drei ehrliche Möglichkeiten:
Ein Flag. Portabel, offensichtlich und ein wenig geschwätzig:
Das Flag taucht an zwei Stellen auf - in der äußeren Bedingung und in der Zuweisung - das ist der Preis. Bei drei Verschachtelungsebenen wird es wirklich unangenehm.
goto. Der viel gescholtene Sprung in C, und das ist der eine Fall, in dem erfahrene C-Programmierer ihn ohne Entschuldigung verwenden:
Ein Vorwärts-goto zu einem Label direkt hinter den Schleifen tut genau das, was ein benanntes break in anderen Sprachen täte, in einer Zeile und ohne ein Flag, das synchron gehalten werden muss. Den Ruf, den goto hat, verdankt es Rückwärts-Sprüngen und Sprüngen mitten in andere Blöcke, die tatsächlich unlesbaren Kontrollfluss erzeugen. Ein einzelner Vorwärtssprung aus verschachtelten Schleifen ist das nicht. Der Linux-Kernel nutzt dasselbe Muster für Aufräumpfade. Verwende es bewusst und sparsam; lass dich vom Tabu nicht in eine Alternative mit drei Flags drängen, die schwerer zu lesen ist.
Eine Funktion und return. Meist die beste Antwort, denn eine Suche verdient einen Namen:
return rollt jede Schleife der Funktion ab, braucht kein Flag und kein Label, und der suchende Code hat nun einen Namen und kann für sich getestet werden. Wenn die Verschachtelung so tief ist, dass das Herauskommen ein Problem wird, ist das oft ein Hinweis darauf, dass der Block ohnehin eine Funktion sein wollte.
Wann sie helfen und wann sie schaden
break und continue verdienen ihren Platz, wenn sie eine Schleife einmal, weit oben, sagen lassen „dieser Fall ist erledigt", statt den ganzen Rumpf in ein if zu wickeln. Vergleiche:
/* mit continue: die eigentliche Arbeit ist nicht eingerückt */
for (int i = 0; i < n; i++) {
if (!isValid(a[i])) continue;
if (isDuplicate(a[i])) continue;
process(a[i]);
}
/* ohne: jede Prüfung fügt eine Ebene hinzu */
for (int i = 0; i < n; i++) {
if (isValid(a[i])) {
if (!isDuplicate(a[i])) {
process(a[i]);
}
}
}
Sie schaden, wenn es viele von ihnen gibt. Eine Schleife mit fünf verstreuten breaks und drei continues hat eine Abbruchbedingung, die nirgends niedergeschrieben steht - ein Leser muss den ganzen Rumpf simulieren, um zu wissen, wann sie stoppt. Zwei Leitlinien, die sich bewähren: Halte die continue-Prüfungen oben im Rumpf beisammen, und wenn eine Schleife mehr als ein oder zwei breaks hat, überlege, ob die Bedingung im Kopf diese Arbeit hätte übernehmen sollen.
Zwei kleinere Fallen. continue in einer do-while-Schleife springt zur Prüfung am Ende, die ohnehin das Nächste ist - harmlos, überrascht aber. Und break außerhalb jeder Schleife oder jedes switch ist ein Übersetzungsfehler - der eine Fehler hier, den der Compiler für dich abfängt.
Häufig gestellte Fragen
Was ist der Unterschied zwischen break und continue in C?
break beendet die Schleife vollständig, und die Ausführung geht danach weiter. continue bricht nur den aktuellen Durchlauf ab und geht zum nächsten über - zur Aktualisierung in einer for-Schleife oder direkt zur Bedingungsprüfung in einer while- oder do-while-Schleife.
Wie breche ich in C aus einer verschachtelten Schleife aus?
Ein einzelnes break verlässt nur die innerste Schleife. Um beide zu verlassen, setze entweder ein Flag und prüfe es in der Bedingung der äußeren Schleife, benutze goto mit einem Label hinter den Schleifen oder - meist am saubersten - verschiebe die Schleifen in eine Funktion und nutze return.
Verlässt break die Schleife oder nur das switch?
Was auch immer es am engsten umschließt. Ein break in einem switch, das in einer Schleife steht, beendet nur das switch; die Schleife läuft weiter. Um aus einem switch heraus die Schleife zu verlassen, brauchst du ein Flag, ein goto oder ein return.
Warum erzeugt continue in meiner while-Schleife eine Endlosschleife?
Weil continue zur Bedingung springt und den Rest des Rumpfes überspringt - einschließlich der Erhöhung des Zählers, wenn diese am Ende steht. In einer for-Schleife steht die Aktualisierung im Kopf und läuft deshalb immer; in einer while-Schleife verschiebe die Aktualisierung vor das continue oder benutze ein for.