6    Schleifen – Programmteile wiederholen

In diesem Kapitel erfahren Sie, wie Sie durch Schleifen Anweisungsblöcke mehrfach ausführen können. Jede »richtige« Programmiersprache bietet die Möglichkeit, Schleifen auszuführen, und ohne Schleifen können Sie quasi keine komplexeren Berechnungsvorschriften (auch Algorithmen genannt) ausführen. Wenn Sie in C eine Gruppe von Anweisungen mehrfach ausführen wollen, stehen Ihnen mit for, while und do-while drei verschiedene Schleifenarten – sogenannte Iterationsanweisungen oder auch Wiederholungen – zur Verfügung.

6.1    Die Zählschleife – for

Die for-Schleife wird häufig als Zählschleife bezeichnet. Es folgt zunächst die Syntax dieser Schleife:

for( Initialisierung; Bedingung; Reinitialisierung ) {
// Anweisung(en)
}

Beim Eintritt in die for-Schleife wird vor dem eigentlichen Schleifendurchlauf einmalig die Initialisierung ausgeführt. Für gewöhnlich wird hierbei die Schleifenvariable initialisiert. Es kann stattdessen aber auch eine beliebige Anweisung ausgeführt werden, was aber nur selten gemacht wird. Bedingung ist die logische Bedingung, die den Schleifenablauf regelt, also die Abbruchbedingung für die Schleife festlegt. Solange Bedingung wahr (ungleich 0) ist, werden die Anweisungen im Schleifenrumpf ausgeführt. Ist Bedingung hingegen unwahr (gleich 0), endet die Schleife, und die Programmausführung wird hinter dem Schleifenrumpf fortgeführt. Der letzte Ausdruck, die Reinitialisierung, wird immer zum Abschluss jedes Schleifendurchgangs ausgeführt. In der Regel wird die Reinitialisierung für die Veränderung der Schleifenvariable genutzt. Allerdings kann hierzu auch eine beliebige Anweisung verwendet werden. Damit sind Schleifen in C sehr viel flexibler als in vielen anderen Programmiersprachen.

Zum besseren Verständnis zeigt Abbildung 6.1 einen logischen Programmablaufplan der for-Schleife.

Programmablaufplan für die übliche Verwendung einer for-Schleife

Abbildung 6.1    Programmablaufplan für die übliche Verwendung einer for-Schleife

Ein Minimalbeispiel soll die for-Schleife demonstrieren:

00  // Kapitel6/for_1.c
01 #include <stdio.h>

02 int main(void) {
03 for( int cnt = 1; cnt <= 5; cnt++ ) {
04 printf("%d. Schleifendurchlauf\n", cnt);
05 }
06 return 0;
07 }

Listing 6.1    for_1.c demonstriert eine einfache Zählschleife.

Der Schleifenablauf spielt sich in den Zeilen (03) bis (05) ab. In der Schleife wird zunächst die Variable cnt mit dem Wert 1 initialisiert. (Sie werden Variablennamen wie cnt oder einfach nur i, j, k, l vor allem in fremden Programmen noch häufiger antreffen.) Anschließend wird überprüft, ob der Wert der Variablen cnt kleiner oder gleich 5 ist. Ist dies der Fall, wird die Anweisung im Schleifenrumpf (Zeile (04)) ausgeführt. Nach der Ausführung der Anweisung wird der dritte Ausdruck der for-Schleife ausgeführt. Hier wird der Wert der Variablen mit cnt++ um den Wert 1 erhöht. Jetzt wird wieder überprüft, ob der Wert von cnt kleiner oder gleich 5 ist.

Der Vorgang wird so lange wiederholt, bis die Abbruchbedingung, dass cnt kleiner oder gleich 5 ist, unwahr (also gleich 0) zurückliefert. Ist dies der Fall, wird der Schleifendurchlauf beendet, und es wird mit der Programmausführung hinter der Schleifenanweisung (hier Zeile (06)) fortgefahren.

Das Programm gibt bei der Ausführung Folgendes aus:

1. Schleifendurchlauf
2. Schleifendurchlauf
3. Schleifendurchlauf
4. Schleifendurchlauf
5. Schleifendurchlauf

Das Beispiel soll nicht darüber hinwegtäuschen, dass die for-Schleife extrem vielseitig und flexibel ist. So können Sie den Schleifendurchlauf in for_1.c auch wie folgt ändern:

for(int cnt=1;cnt<=5;printf("%d. Schleifendurchlauf\n",cnt++));

Hier wurde die Reinitialisierung der Schleifenvariablen gleichzeitig mit der printf()-Anweisung im dritten Argument der for-Schleife verwendet. So können Sie sich den Schleifenrumpf hier auch gleich sparen und mit einem Semikolon abschließen. Allerdings sollte dieses Beispiel keine Schule machen. Es dient nur zu Demonstrationszwecken, weil Sie auch solche Dinge immer wieder und vor allem in fremden Programmen antreffen können. Und da man heutzutage vor allem im Team programmiert, sollten Sie auch fremden Code lesen können, der nicht so sauber strukturiert ist, wie Ihr eigener.

Neben der Möglichkeit, verschiedene Ausdrücke in der for-Schleife zu verwenden, können auch Ausdrücke fehlen. Entscheidend ist, dass die beiden Semikola in den Klammern an der richtigen Stelle vorhanden sind. Alle folgenden Beispiele sind erlaubt:

// ohne 1. Ausdruck
for( ; i < 10; i++ ) { ... }
// ohne 1. und 3. Ausdruck
for( ; i < 10; ) { ... }
// ohne einen Ausdruck – eine Endlosschleife
for( ;; ) { ... }

Beachten Sie allerdings, dass eine Schleifenvariable, die Sie innerhalb von for definiert haben, nicht mehr nach dem Ende des Anweisungsblocks der for-Schleife zur Verfügung steht. Sie haben dann allerdings gegebenenfalls auch den Vorteil, ein versehentliches Semikolon am Ende einer for-Schleife zu entdecken, beispielsweise:

for(int i=0; i<10; i++);   // <- fehlerhaftes Semikolon
printf("%d\n", i); // wird erst nach der Schleife ausgeführt

Hier war wohl eher geplant, printf() bei jedem Schleifendurchlauf aufzurufen, um den aktuellen Wert von i auszugeben. Stattdessen wird aufgrund des (hier) irrtümlich gesetzten Semikolons am Ende von for die Schleife zwar ausgeführt, aber erst danach printf() nur ein einziges Mal mit dem aktuellen Wert von i aufgerufen und ausgegeben. Das Beispiel enthält keinen syntaktischen, sondern einen logischen Fehler, der wesentlich schwieriger aufzufinden ist, weil für den Compiler alles in Ordnung ist.

Definieren Sie hingegen die Schleifenvariable i innerhalb von for, würde der Compiler sich mit einer Fehlermeldung beschweren, wenn Sie irrtümlicherweise ein Semikolon am Ende von for verwendet hätten, weil die Variable nicht mehr im Gültigkeitsbereich läge:

for(int i=0; i<10; i++);   // <- fehlerhaftes Semikolon
printf("%d\n", i); // Fehler: i ist hier unbekannt

Es ist auch möglich, im Schleifenkopf von for mehrere Ausdrücke mit dem Kommaoperator getrennt zu verwenden. In der Praxis werden so z. B. in einer Anweisung (vor dem ersten Semikolon) mehrere Variablen initialisiert und/oder in der letzten Anweisung mehrere Variablen reinitialisiert. Hierzu finden Sie im Folgenden ein einfaches Beispiel:

00  // Kapitel6/for_2.c
01 #include <stdio.h>

02 int main(void) {
03 for( int i=1, j=10; i < j; i++, j--) {
04 printf("i=%d, j=%d\n", i, j);
05 }
06 return 0;
07 }

Listing 6.2    for_2.c demonstriert die Initialisierung mehrerer Zählvariablen innerhalb des Schleifenkopfes.

Im Schleifenkopf in Zeile (03) werden die beiden int-Variablen i und j zunächst mit einer Initialisierungsanweisung mit Werten initialisiert. Im Reinitialisierungsausdruck werden die Werte dieser Variablen dann inkrementiert bzw. dekrementiert. Im letzten Beispiel wird dann einfach der eine Wert hoch- und der andere heruntergezählt, bis i nicht mehr kleiner als j ist (was die Abbruchbedingung der Schleife ist). In der Praxis findet man solche Schleifenkonstrukte in Such- oder Sortieralgorithmen. Manchmal (z. B. beim GCC unter Linux) sind solche Ausdrücke wie for int i=0; i<10; i++ nicht erlaubt und enden mit dem Hinweis, dass Sie gerade zum C99-Standard inkompatiblen Code erstellen wollen. In diesem Fall müssen Sie sämtliche Variablen stets am Anfang einer Funktion initialisieren, oder aber das entsprechende Flag std=C11 benutzen. Oder noch einfacher: Sie verwenden den C++-Compiler und erstellen sämtliche Beispiele mit C++ beispiel.cpp -o beispiel.

Das letzte Beispiel sieht bei der Ausführung so aus:

i=1, j=10
i=2, j=9
i=3, j=8
i=4, j=7
i=5, j=6

Erwähnt werden sollte noch, dass Sie for-Schleifen natürlich auch ineinander verschachteln können. Solche verschachtelten Schleifen finden Sie ebenfalls häufiger in Such- oder Sortieralgorithmen. Hierzu finden Sie im Folgenden ein einfaches Beispiel:

for( int i = 0; i < 10; i++ ) {
for( int j = 0; j < 10; j++ ) {
printf("i=%d, j=%d\n", i, j);
}
}