Menu

For-Schleife in C: Syntax, Beispiele und häufige Fehler

Wie man Code mit der C-for-Schleife wiederholt - der dreiteilige Kopf, hoch- und runterzählen, Arrays mit dem sizeof-Trick durchlaufen, verschachtelte Schleifen, Endlosschleifen sowie die Off-by-one- und Unsigned-Bugs, die jeden erwischen.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Warum eine for-Schleife

if und switch wählen einen Zweig und führen ihn einmal aus. Echte Programme müssen Dinge wiederholt tun: jede Punktzahl ausgeben, eine Liste von Zahlen summieren, zehn Zeilen eines Rasters zeichnen, jedes Zeichen einer Zeichenkette prüfen. Die for-Schleife ist das Arbeitstier von C, um Code eine bekannte Anzahl von Malen zu wiederholen, mit einem Zähler, den du steuerst.

Alles, was eine for-Schleife braucht, steckt in einem kompakten Kopf, sodass „wie oft und wie" in einer einzigen Zeile sichtbar ist.

Der dreiteilige Kopf

Ein for-Kopf hat drei durch Semikolons getrennte Teile: eine Initialisierung, eine Bedingung und eine Aktualisierung.

for (initializer; condition; update) {
    // Rumpf - läuft, solange die Bedingung ungleich null ist
}

Sie werden in einer bestimmten Reihenfolge ausgeführt, und wer sie kennt, versteht jede for-Schleife, die ihm je begegnet:

  1. Die Initialisierung läuft einmal, vor allem anderen.
  2. Die Bedingung wird geprüft. Ist sie null, endet die Schleife sofort.
  3. Der Rumpf läuft.
  4. Die Aktualisierung läuft.
  5. Zurück zu Schritt 2.

Verfolge die obige Schleife mit dieser Liste: int i = 0 läuft einmal. 0 < 5 gilt, also gibt der Rumpf i = 0 aus, dann macht i++ aus i eine 1. Die Bedingung wird erneut geprüft, und so weiter. Wenn i den Wert 5 erreicht, schlägt die Prüfung fehl, der Rumpf wird übersprungen und fertig wird ausgegeben. Der Rumpf lief genau fünfmal, wobei i die Werte 0 bis 4 annahm.

Zwei Konsequenzen folgen direkt daraus. Die Bedingung wird vor dem ersten Durchlauf geprüft, eine Schleife, deren Bedingung von Anfang an falsch ist, läuft also null Mal - for (int i = 10; i < 5; i++) betritt den Rumpf überhaupt nie. Und die Aktualisierung läuft nach dem Rumpf, also hält i im gesamten Rumpf noch den alten Wert.

Den Zähler im Kopf zu deklarieren (int i = 0) ist seit C99 erlaubt und die richtige Voreinstellung: i existiert dann nur innerhalb der Schleife, kann also nicht mit etwas danach kollidieren, und jede Schleife in einer Funktion darf den Namen frei wiederverwenden.

Hoch-, runter- und in Schritten zählen

Die Aktualisierung ist nicht auf i++ beschränkt. Zähle runter, rücke in beliebiger Schrittweite vor oder verdopple bei jedem Durchlauf:

Passe die Bedingung zur Aktualisierung. Hochzählen passt zu < oder <=; Runterzählen passt zu > oder >=. Diese Paarung falsch zu wählen ist der Weg zu einer Schleife, die nie endet.

Beachte, dass die zweite Schleife <= 10 verwendet, weil 10 ein Wert ist, den wir einschließen wollen, während das erste Beispiel < 5 nutzte, weil 5 es nicht ist. An dieser Entscheidung werden Off-by-one-Bugs geboren, triff sie also bewusst: i < n läuft n-mal ab 0; i <= n läuft n + 1-mal.

Über ein Array iterieren

Die häufigste Verwendung einer Zählschleife ist das Durchlaufen eines Arrays per Index. Der Zähler dient zugleich als die Position, die du liest.

Zwei Dinge sind hier mitzunehmen.

Der sizeof-Trick. sizeof(scores) ist die Größe des gesamten Arrays in Bytes und sizeof(scores[0]) die Größe eines Elements, ihr Quotient ist also die Elementanzahl. Er passt sich automatisch an, wenn du ein Element hinzufügst - was eine fest eingetragene 5 nicht tut. Der Haken - und er ist groß - ist, dass er nur dort funktioniert, wo das Array selbst sichtbar ist. Übergib das Array an eine Funktion und es zerfällt zu einem Zeiger, sodass sizeof dort stattdessen die Größe eines Zeigers liefert. Nimm innerhalb einer Funktion die Länge immer als separaten Parameter entgegen.

Die Bedingung lautet i < n, niemals i <= n. Ein Array mit fünf Elementen hat die gültigen Indizes 0 bis 4. scores[5] zu lesen ist undefiniertes Verhalten: Es kann Müll ausgeben, abstürzen oder scheinbar funktionieren und dabei still etwas anderes beschädigen. C prüft Grenzen überhaupt nicht, das liegt also vollständig bei dir.

Ein Array rückwärts zu durchlaufen ist dieselbe Idee in umgekehrter Richtung:

Beginne bei n - 1 (dem letzten gültigen Index), stoppe einschließlich 0 - daher >= und nicht >. Halte i hier als vorzeichenbehaftetes int; der nächste Abschnitt erklärt, warum.

Verschachtelte Schleifen

Setze ein for in ein anderes, um mit Rastern, Tabellen oder jedem Paar von Elementen zu arbeiten. Die innere Schleife läuft bei jedem einzelnen Schritt der äußeren Schleife vollständig durch.

Die äußere Schleife fixiert eine row; die innere Schleife durchläuft jede col für diese Zeile; das printf("\n") nach der inneren Schleife beendet die Zeile. Verschiebe diesen Zeilenumbruch in die innere Schleife, und die ganze Tabelle wird zu einer einzigen Spalte - einen Versuch wert, weil es die Verschachtelungsstruktur greifbar macht.

Gib den Zählern verschiedene Namen. row/col oder i/j sind in Ordnung; i für beide zu verwenden verdeckt den äußeren Zähler und erzeugt verblüffende Ergebnisse. Und achte auf die Kosten: Eine n-Schleife in einer n-Schleife führt den Rumpf n * n-mal aus, zwei Schleifen mit je 1.000 Iterationen sind also eine Million Durchläufe.

Hier eine verschachtelte Schleife, die echte Arbeit leistet - ein Dreieck aus Sternen, bei dem die Grenze der inneren Schleife vom äußeren Zähler abhängt:

break und continue

Zwei Schlüsselwörter ändern den Ablauf mitten in der Schleife. break verlässt die Schleife sofort; continue überspringt den Rest des aktuellen Durchlaufs und springt zur Aktualisierung.

Die erste Schleife stoppt in dem Moment, in dem sie 7 findet, und prüft die restlichen 93 Werte nie. Die zweite nutzt continue, um die Ausgabe für gerade Zahlen zu überspringen; das i++ im Kopf läuft trotzdem, die Schleife rückt also weiter vor. Viel mehr zu beidem findest du unter break und continue, einschließlich der Frage, wie man aus einer verschachtelten Schleife herauskommt, was ein einzelnes break nicht kann.

Endlosschleifen und leere Teile

Alle drei Teile des Kopfes sind optional. Lässt du die Bedingung weg, gilt sie als dauerhaft wahr:

for (;;) {
    /* läuft ewig - Ausstieg mit break oder return */
}

for (;;) ist die idiomatische Endlosschleife in C, und die Semikolons sind weiterhin erforderlich. Das ist kein Fehler, solange etwas darin break, return oder exit ausführen kann - Ereignisschleifen und „frag weiter, bis die Eingabe gültig ist"-Schleifen werden genau so geschrieben.

Auch andere Teile darfst du weglassen. Existiert der Zähler bereits, lass die Initialisierung weg; rückt der Rumpf ihn vor, lass die Aktualisierung weg:

int i = 0;
for (; i < n; ) {
    /* ... */
    i += step;
}

Das ist erlaubt, liest sich aber schlechter als die while-Schleife, die es in Wahrheit ist. Bevorzuge ein for, wenn Zähler, Grenze und Schrittweite zusammen in den Kopf gehören, und eine while-Schleife, wenn nicht.

Der Komma-Operator im Kopf

Initialisierung und Aktualisierung dürfen jeweils mehrere durch Kommas getrennte Ausdrücke enthalten - so lässt du zwei Zähler gleichzeitig laufen:

int i = 0, j = len - 1 deklariert beide Zähler und i++, j-- rückt beide vor. Das ist der Komma-Operator, der seine Operanden der Reihe nach auswertet. Nutze ihn für wirklich parallele Zähler; unzusammenhängende Arbeit in einen Kopf zu quetschen versteckt sie nur.

Häufige Fallstricke

Eine Handvoll Fallen erklärt die meisten for-Schleifen-Bugs in C.

Off-by-one. i <= n liest bei einem nullbasierten Array ein Element hinter dem Ende. Verwende i < n für „n-mal, beginnend bei 0".

Ein Semikolon nach dem Kopf. Das kompiliert und ist fast nie das, was du meintest:

/* BUG: der Schleifenrumpf ist die leere Anweisung; das printf läuft einmal */
for (int i = 0; i < 5; i++);
{
    printf("%d\n", i);
}

Das ; wird zum Rumpf, also dreht sich die Schleife fünfmal ohne etwas zu tun, und die Klammern darunter laufen einmal als schlichter Block. gcc -Wall warnt davor.

Vorzeichenlose Zähler, die runterzählen. Das ist eine Endlosschleife:

/* BUG: ein vorzeichenloser Wert ist nie < 0 */
for (size_t i = n - 1; i >= 0; i--) {
    process(a[i]);
}

i >= 0 ist für einen vorzeichenlosen Typ immer wahr. Wenn i den Wert 0 erreicht, springt i-- auf eine riesige positive Zahl um, und die Schleife läuft weiter in Speicher hinein, der ihr nicht gehört. Verwende für absteigende Zählungen ein vorzeichenbehaftetes int, oder schreibe die Bedingung als for (size_t i = n; i-- > 0; ), was in einem Schritt verringert und prüft und korrekt bei null stoppt.

Den Zähler an zwei Stellen verändern. i sowohl im Rumpf als auch im Kopf zu ändern macht die Anzahl der Durchläufe unvorhersehbar. Entscheide dich für einen Ort.

Gleitkommazähler. for (double x = 0.0; x != 1.0; x += 0.1) terminiert womöglich nie, weil 0.1 keine exakte binäre Darstellung hat und die Summe an 1.0 vorbeiläuft, ohne sie zu treffen. Iteriere mit einer Ganzzahl und berechne den Wert im Inneren:

for (int i = 0; i <= 10; i++) {
    double x = i / 10.0;
    /* ... */
}

Die Länge eines Arrays während der Schleife ändern. Die Bedingung i < n liest n bei jedem Durchlauf neu; wenn der Rumpf also die Sammlung verkleinert, passe den Index bewusst an, statt die beiden auseinanderdriften zu lassen.

Häufig gestellte Fragen

Wie schreibt man eine for-Schleife in C?

Setze drei durch Semikolons getrennte Teile in den Kopf - eine Initialisierung, eine Bedingung und eine Aktualisierung: for (int i = 0; i < 5; i++) { printf("%d\n", i); }. Das führt den Rumpf fünfmal aus, wobei i die Werte 0 bis 4 annimmt, und stoppt, sobald die Bedingung falsch ist.

Wie durchlaufe ich ein Array in C?

Zähle einen Index von 0 bis (ausschließlich) zur Länge: for (int i = 0; i < n; i++) { sum += a[i]; }. Für ein Array, das in derselben Funktion deklariert wurde, kannst du die Länge mit sizeof(a) / sizeof(a[0]) berechnen; sobald das Array an eine Funktion übergeben wurde, funktioniert dieser Trick nicht mehr - dann übergibst du die Länge als Parameter.

Warum läuft meine C-for-Schleife einmal zu oft?

Das ist der Off-by-one-Fehler. Bei einem nullbasierten Array der Größe n sind die gültigen Indizes 0 bis n - 1, also führt i <= n eine zusätzliche Iteration aus und liest hinter das Ende - undefiniertes Verhalten. Die sichere Voreinstellung ist i < n.

Kann man die Schleifenvariable innerhalb einer C-for-Schleife deklarieren?

Ja, seit C99: for (int i = 0; i < n; i++). Die Variable existiert dann nur innerhalb der Schleife, was genau das ist, was du willst. Kompiliere mit gcc -std=c99 oder neuer (moderne GCC- und clang-Versionen verwenden standardmäßig C17, es funktioniert also einfach); im alten C89-Stil wurde i vor der Schleife deklariert.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S