19. Iteration

while

while

-Schleifen funktionieren in C genau wie in Python:

while (a>5) {
  printf("Größer als 5: %d\n", a--);
}

break und continue

Analog zu Python gibt es break, das die engste umschließende Schleife verlässt. In längeren Schleifen liberal benutzt führt es zu Chaos.

Gleiches gilt für continue, das aus dem Schleifenkörper unter Auslassung seines Rests direkt zur Schleifenbedingung springt.

Beispiel dazu:

while (1) {
  if (!fgets(str, LINE_LEN, inputFile)) {
    break;
  }
  if (str[0]==0) {
    continue;
  }
  printf("%s", str);
}

In diesem Beispiel liefert str[0] das erste Zeichen von str. Wenn dieses Zeichen 0 (nicht ’0’!) ist, wird die Zeile nicht gedruckt. Im wirklichen Leben bedeutet das, dass das Programmfragment alle Zeilen außer den ganz leeren wieder ausgibt.

do-while

Abhänging von einer Bedingung wird ein Block mindestens einmal ausgeführt, der Test auf die Schleifenbedingung findet am Ende statt.

do {
  fgets(str, LINE_LEN, stdin);
  printf("%s", str);
} while (!feof(stdin));

Dieses Fragment illustriert eine der häufigsten Fallen bei akzeptierenden Schleifen, nämlich, dass die Logik der meisten Probleme so ist, dass die Schleife eventuell eben gar nicht durchlaufen werden sollte (oder auch beim ersten Mal nur teilweise). Hier prüft das feof in der while-Bedingung am Ende letztlich, ob das fgets fehlerfrei arbeiten kann (außerdem würde das Programm angesetzt auf eine leere Datei möglicherweise abstürzen). Generell gibt es wenig wirklich gute Anwendungen für eine Schleife mit Endbedingung, fast immer ist es verständlicher, eine Schleife mit break zu konstruieren.

Der tiefere Grund liegt in der goldenen Regel, Fehlerbedingungen (in dem Fall: “Es gibt nichts mehr zu lesen”) dort abzufragen, wo sie entstehen, hier also beim fgets – freundlicherweise gibt fgets den speziellen Wert NULL zurück, wenn es nichts mehr lesen konnte – deshalb stürzt das Programm oben auch ab, wenn die Datei leer ist, denn das printf versucht dann, NULL zu drucken, was zum Absturz führt. Das Programm oben sollte jedenfalls besser so aussehen:

while (1) { /* Endlosschleife, wir breaken nachher raus */
  if (!fgets(str, LINE_LEN, stdin) {
    break;
  }
  printf("%s", str);
}

Python hat keine akzeptierende Schleife, auch wenn do-while natürlich trivial zu simulieren ist:

while 1:
  ... loop body ...
  if condition:
    break

for

Weil C keine eingebauten Sequenzen hat, muss die for-Schleife etwas anders aussehen, genauer:

forLoop ::= "for" "(" [initExpression ]";"
  [logicalExpression ] ";"
  [updateExpression ]")" CmpdStmt

Dabei werden im initStatement Schleifenvariablen gesetzt, die Schleife wird abgebrochen, wenn die logicalExpression false wird, und bei jeder Wiederholung der Schleife wird die updateExpression ausgeführt.

Jede der drei Ausdrücke ist optional, kann also weggelassen werden – for (;a<10;) ist z.B. äquivalent zu while(a<10).

Die Standardschleife

for i in range(n):
  foo(i)

sieht also in C so aus:

for (i=0; i<n; i++) {
  foo(i);
}

In gewisser Weise ist die for-Schleife in C ein allgemeineres Konstrukt als die for-Schleife in Python – es lassen sich wie gesagt alle while-Schleife auch als for-Schleifen ausdrücken.

Das hat auch Nachteile, weil die for-Schleife auf diese Weise gerne missbraucht wird. Generell sollte eine for-Schleife auch in C nur dann eingesetzt werden, wenn man über eine vorher bekannte Zahl von “Objekten” iterieren will, also in etwa das hat, was in Python eine Sequenz ist.

Noch eine Warnung: Aus Gründen, die bei der Behandlung von Arrays klar werden sollten, sind Schleifenbedingungen der Art i<=n immer verdächtig, in der Regel sollten sie die Form i<n haben.

Ein weiteres Beispiel zu for:

#include <stdio.h>

int main(void)
{
  char c;

  for (c='a'; c<='z'; c++) {
    fputc(c, stdout);
  }
  fputc('\n', stdout);
  return 0;
}

Anmerkung: Es gibt keine Garantie, dass das Programm wirklich alle und nur die Kleinbuchstaben des Alphabets ausgibt. Auf IBM-Großrechnern beispielsweise wäre das unter Umständen nicht der Fall, und natürlich auch nicht auf Maschinen mit kyrillischem Zeichensatz. Offenbar fehlen auch die Umlaute. All diese Probleme sind potentiell lösbar – ihr ahnt schon, dass hier wieder mal Locales und Encodings lauern. Der Umgang mit ihnen ist in C deutlich komplizierter als in Python, und meine Empfehlung ist, Aufgaben, in denen man ASCII signifikant verlässt, in Python oder mithilfe geeigneter Bibliotheken (z.B. der noch zu besprechenden glib) zu erledigen.

Übungen zu diesem Abschnitt

Ihr solltet euch wenigstens an den rötlich unterlegten Aufgaben versuchen

(1)

Schreibt mit fgetc und fputc – wie in der einfachen Ein- und Ausgabe dargestellt – ein Programm, das seine Standardeingabe auf seine Standardausgabe kopiert, bis das Dateiende kommt. Wenn ihr eure Quelldatei copyi2o genannt habt, könnt ihr es folgendermaßen ausprobieren:

copyi2o < copyi2o.c

Euer Quelltext sollte ausgegeben werden, das Programm dann anhalten.


Markus Demleitner

Copyright Notice