6. Kontrollstrukturen

Mit Kontrollstrukturen sind die Befehle und Zeichenfolgen gemeint, die den Ablauf des Programms steuern. Dazu gehören im wesentlichen verschiedene Verzweigungen und Schleifen.

6.1. Sequenzen

Diese Struktur ist schon bekannt: das Semikolon (";"). Jede Anweisung muss mit einem Semikolon abgeschlossen sein, damit der C-Compiler weiss, an welcher Stelle die Anweisung zu Ende ist.

Es können mehrere Operationen hintereinander gestellt werden, wenn sie durch Kommata voneinander getrennt werden. Diese Operationen werden von links nach rechts ausgewertet; das Ergebnis der letzten Operation ist gleichzeitig das Ergebnis der gesamten Operation. Diese Eigenschaft wird weiter unten bei der for-Schleife ausgenutzt.

Beispiel:

kap06_01.c

01 #include <stdio.h>
02
03 int main()
04 {
05    char c_alt, c_neu, c = 'z';
06
07    c_neu = (c_alt = c, c = 'a');
08    printf("c = %c\n", c);
09    printf("c_alt = %c\n", c_alt);
10    printf("c_neu = %c\n", c_neu);
11
12    return 0;
13 }

Ausgegeben wird:

c = a
c_alt = z
c_neu = a


In der Klammer wird zuerst der Inhalt von c der Variable c_alt zugewiesen ('z'). Dann erhält c den neuen Wert ('a'). Dieser Wert ist gleichzeitig das Ergebnis des gesamten Klammerausdrucks und wird der Variablen c_neu zugewiesen.

Hinweis:
Die Klammer ist dringend notwendig, da der Komma-Operator die geringste Priorität hat. Würde die Klammer weggelassen werden, entspräche es folgender Zeile:

   (c_neu = c_alt = c), c = 'a';

c_neu hätte damit das 'z' und nicht das 'a' als Inhalt.

6.2. Verzweigung: if-Anweisung

Oft ist es nötig, Anweisungen in Abhängigkeit von Bedingungen auszuführen. Der einfachste Befehl zum Abfragen von Bedingungen und anschließendem Verzweigen in Abhängigkeit der Bedingung ist die if-Anweisung. Sie hat folgende Syntax:

if (Bedingung)
   Anweisung1;
else
   Anweisung2;

Gelesen wird dieser Block folgendermaßen: Wenn die Bedingung wahr ist, dann führe Anweisung1 aus, sonst führe Anweisung2 aus. Dabei kann der else-Teil (also else Anweisung2) weggelassen werden. Wichtig ist, dass nur hinter den Anweisungen ein Semikolon gesetzt wird aber nicht hinter if (Bedingung) und else.

Beispiel:

kap06_02.c

01 /***********************************************
02  * Dieses Programm liest drei ganze Zahlen ein
03  * und ermittelt das Maximum der drei Zahlen.
04  * Eingabe: Zahl1, Zahl2, Zahl3
05  * Ausgabe: Maximum
06  ***********************************************/
07 #include <stdio.h>
08
09 int main()
10 {
11    long Zahl1, Zahl2, Zahl3, Maximum;
12
13    printf("Dieses Programm ermittelt das Maximum ");
14    printf("von drei eingegebenen Zahlen.\n");
15    printf("Bitte geben Sie drei ganze Zahlen ein: ");
16    scanf("%ld %ld %ld", &Zahl1, &Zahl2, &Zahl3);
17    if (Zahl1 > Zahl2)
18       Maximum = Zahl1;
19    else
20       Maximum = Zahl2;
21    if (Zahl3 > Maximum)
22       Maximum = Zahl3;
23    printf("Das Maximum der drei Zahlen lautet: ");
24    printf("%li\n", Maximum);
25
26    return 0;
27 }

Um anstelle der Anweisung1 und Anweisung2 mehrere Anweisungen durchführen zu lassen, müssen diese Anweisungen jeweils in einen Block geschrieben werden, d.h. sie müssen in geschweifte Klammern gesetzt werden. Die Syntax sieht dann folgendermaßen aus:

if (Bedingung)
{
   Anweisung1a;
   Anweisung1b;
   Anweisung1c;
}
else
{
   Anweisung2a;
   Anweisung2b;
}


if-Anweisungen können auch geschachtelt werden. Dabei muss allerdings sehr genau darauf geachtet werden, welches else zu welchem if gehört. Durch das Einrücken der Anweisungen wird die Zugehörigkeit sehr deutlich sichtbar.

if (Bedingung)
{
   Anweisung1;
   if (Bedingung1)
   {
      Anweisung11a;
      Anweisung11b;
   }
   else
   {
      Anweisung12a;
      Anweisung12b;
   }
}
else
{
   if (Bedingung2)
   {
      Anweisung21a;
      if (Bedingung21)
         Anweisung211;
   }
   else
   {
      Anweisung22a;
      Anweisung22b;
   }
   Anweisung2;
}


Beispiel:

kap06_03.c

01 /***********************************************
02  * Dieses Programm liest vier ganze Zahlen ein
03  * und ermittelt das Maximum der vier Zahlen.
04  * Eingabe: Zahl1, Zahl2, Zahl3, Zahl4
05  * Ausgabe: Maximum
06  ***********************************************/
07 #include <stdio.h>
08
09 int main()
10 {
11    long Zahl1, Zahl2, Zahl3, Zahl4, Maximum;
12
13    printf("Dieses Programm ermittelt das Maximum ");
14    printf("von vier eingegebenen Zahlen.\n");
15    printf("Bitte geben Sie vier ganze Zahlen ein: ");
16    scanf("%ld %ld %ld %ld", &Zahl1, &Zahl2, &Zahl3, &Zahl4);
17    if (Zahl1 > Zahl2)
18       if (Zahl3 > Zahl4)
19          if (Zahl1 > Zahl3)
20             Maximum = Zahl1;
21          else
22             Maximum = Zahl3;
23       else
24          if (Zahl1 > Zahl4)
25             Maximum = Zahl1;
26          else
27             Maximum = Zahl4;
28    else
29       if (Zahl3 > Zahl4)
30          if (Zahl2 > Zahl3)
31             Maximum = Zahl2;
32          else
33             Maximum = Zahl3;
34       else
35          if (Zahl2 > Zahl4)
36             Maximum = Zahl2;
37          else
38             Maximum = Zahl4;
39    printf("Das Maximum der vier Zahlen lautet: ");
40    printf("%li\n", Maximum);
41
42    return 0;
43 }

Eine Mehrfachverzweigung kann als eine Serie von if-else-Anweisungen angesehen werden. Dabei ist in jedem (außer im letzten) else-Zweig eine weitere if-Anweisung enthalten. Dies sieht wie folgt aus:

   if (Bedingung1)
      Anweisung1;
   else
      if (Bedingung2)
         Anweisung2;
      else
         if (Bedingung3)
            Anweisung3;
         else
            Anweisung4;

Dabei kann es bei vielen Verzweigung dazu führen, dass der Quelltext sehr weit nach rechts eingerückt wird. Dies führt beim Ausdrucken unter Umständen zu sehr unleserlichen Quelltexten und auch auf dem Bildschirm muss unter Umständen viel nach links und rechts geblättert werden. Daher sollte die Einrückung bei Mehrfachverzweigungen etwas variiert werden:

   if (Bedingung1)
      Anweisung1;
   else if (Bedingung2)
      Anweisung2;
   else if (Bedingung3)
      Anweisung3;
   else
      Anweisung4;

6.3. Verzweigung: ? : -Anweisung

Diese Anweisung ist eine Kurzform der if-Anweisung und sieht wie folgt aus:

(Bedingung) ? Anweisung1 : Anweisung2;

Die entsprechende if-Anweisung sieht folgendermaßen aus:

if (Bedingung)
   Anweisung1;
else
   Anweisung2;


Aber nicht nur Anweisungen sondern auch einfache Ausdrücke (wie Zahlen, Zeichen, usw.) lassen im Gegensatz zur if-Anweisung hier einsetzen. Als Ergebnis kann dann der erste oder der zweite Ausdruck einer Variablen zugeordnet oder in einer Abfrage verwendet werden. Dadurch können kleine Verzweigungen in einer Zeile geschrieben werden. Das nächste Beispiel greift noch einmal die erste Variante der Maximumsbestimmung auf. Jetzt werden aber die if-Anweisungen durch ?:-Anweisungen ersetzt.

Beispiel:

kap06_04.c

01 /***********************************************
02  * Dieses Programm liest drei ganze Zahlen ein
03  * und ermittelt das Maximum der drei Zahlen.
04  * Eingabe: Zahl1, Zahl2, Zahl3
05  * Ausgabe: Maximum
06  ***********************************************/
07 #include <stdio.h>
08
09 int main()
10 {
11    long Zahl1, Zahl2, Zahl3, Maximum;
12
13    printf("Dieses Programm ermittelt das Maximum ");
14    printf("von drei eingegebenen Zahlen.\n");
15    printf("Bitte geben Sie drei ganze Zahlen ein: ");
16    scanf("%ld %ld %ld", &Zahl1, &Zahl2, &Zahl3);
17    Maximum = (Zahl1 > Zahl2) ? Zahl1 : Zahl2;
18    Maximum = (Zahl3 > Maximum) ? Zahl3 : Maximum;
19    printf("Das Maximum der drei Zahlen lautet: ");
20    printf("%li\n", Maximum);
21
22    return 0;
23 }

6.4. Fallunterscheidung: switch-Anweisung

Die if-Anweisung ist besonders für Aufgaben geeignet, die nur ein oder zwei Bedingungen betreffen. Durch mehrere gleichwertige Bedingungen (d.h. als Bedingung wird immer wieder die gleiche Variable abgefragt) wird der Programmcode durch die Verschachtelung schnell unübersichtlich. Hierfür gibt es die switch-Anweisung. Ihre Syntax sieht wie folgt aus:

switch (Ausdruck)
{
   case Wert1:
      Anweisungen1;
      break;
   case Wert2:
      Anweisungen2;
      break;
   case Wert3:
      Anweisungen3;
      break;
   ...
   default:
      Ersatzanweisungen;
}


Der Datentyp von Ausdruck muß eine ganze Zahl (int) sein oder muss sich in eine ganze Zahl umwandeln lassen, z.B. ein Zeichen (char). Reelle Zahlen und Zeichenketten sind nicht zugelassen! Die Werte hinter dem case müssen konstante Werte sein. Dabei können die Werte als ganze Zahl oder als Zeichen angegeben werden.

Mit Hilfe des break; wird die switch-Anweisung beendet. Andernfalls werden die Anweisungen der weiteren case-Zeilen auch durchgeführt. Dieses kann auch dazu verwendet werden, um verschiedenen Fälle zusammenzufassen, d.h. in verschiedenen Fällen werden die gleichen Anweisungen ausgeführt (siehe das folgende Beispiel).

Beispiel:

kap06_05.c

01 /***********************************************
02  * Dieses Programm fragt das Geschlecht ab und
03  * gibt das Ergebnis auf dem Bildschirm aus.
04  * Eingabe: c
05  * Ausgabe: "weiblich" oder "maennlich"
06  ***********************************************/
07 #include <stdio.h>
08
09 int main()
10 {
11    char c;
12
13    printf("Geben Sie bitte Ihr Geschlecht ein.\n");
14    printf("(w: weiblich, m: maennlich)");
15    scanf("%c", &c);
16    switch (c)
17    {
18       case 'w':
19       case 'W':
20          printf("weiblich\n");
21          break;
22       case 'm':
23       case 'M':
24          printf("maennlich\n");
25          break;
26       default:
27          printf("unbekannt\n");
28    }
29
30    return 0;
31 }

Neben der break-Anweisung kann die switch-Anweisung auch mit den Anweisungen continue (siehe Abschnitt break und continue in diesem Kapitel), goto und return beendet werden.

Die Reihenfolge, in der der switch-Ausdruck mit den case-Ausdrücken verglichen wird, ist nicht definiert. Auch die Umsetzung der Vergleiche in die Maschinensprache hängt von der Anzahl und den Werten der case-Ausdrücke ab. D.h., die switch-Anweisung kann nicht als Folge von if-Anweisungen angesehen werden!

Die Anzahl der case-Ausdrücke, die in einer switch-Anweisung verwendet werden dürfen, ist begrenzt. In C89 dürfen maximal 257, in C99 maximal 1023 case-Ausdrücke verwendet werden.

Obwohl für den Datentypen des Ausdrucks alle Typen von ganzen Zahlen zugelassen sind, gibt es einige ältere Compiler, die die Datentypen long und unsigned long für den Ausdruck nicht zulassen.

6.5. Schleifen: while-Schleifen

Soll ein Programmteil mehrmals wiederholt werden, wird eine sogenannte Schleife verwendet. In C gibt es drei verschiedene Schleifen: while-, do-while- und for-Schleifen. Die beiden letzten lassen sich auch durch die while-Schleife darstellen.

Die while-Schleife hat folgende Syntax:

while (Bedingung)
   Anweisung;


Ähnlich wie bei der if-Anweisung sollte die Anweisung eingerückt werden, um schneller zu erkennen, an welcher Stelle die while-Schleife zu Ende ist. Sollen mehrere Anweisungen in einer Schleife wiederholt werden, werden diese (wie bei der if-Anweisung) in einen Block geschrieben. Dies sieht dann wie folgt aus:

while (Bedingung)
{
   Anweisung1;
   Anweisung2;
   Anweisung3;
}


Dabei wird die Anweisung bzw. der Anweisungsblock so lange wiederholt ausgeführt, bis die Bedingung nicht mehr wahr ist. Ist die Bedingung von vornherein nicht erfüllt, wird die Anweisung bzw. der Anweisungsblock in der while-Schleife überhaupt nicht ausgeführt. Anders herum: Wird die Bedingung niemals falsch, wird die Schleife auch niemals beendet. Dies wird eine Endlosschleife genannt.

Beispiel:

kap06_06.c

01 /*******************************************
02  * Dieses Programm berechnet die Fakultaet
03  * einer eingegebenen Zahl und gibt das
04  * Ergebnis auf dem Bildschirm aus.
05  * Eingabe: Zahl
06  * Ausgabe: Fakult
07  *******************************************/
08 #include <stdio.h>
09
10 int main()
11 {
12    unsigned int Zahl, Zaehler = 1;
13    unsigned long Fakult = 1;
14
15    printf("Dieses Programm berechnet die ");
16    printf("Fakultaet einer ganzen Zahl.\n");
17    printf("Geben Sie bitte eine ganze Zahl ein: ");
18    scanf("%d", &Zahl);
19    while (Zaehler <= Zahl)
20    {
21       Fakult = Fakult * Zaehler;
22       Zaehler++;
23    }
24    printf("%u! = %lu\n", Zahl, Fakult);
25
26    return 0;
27 }

Mit Hilfe der Kurzform-Operatoren und der Inkrementierung von Variablen kann die while-Schleife drastisch verkürzt werden:

   while (Zaehler <= Zahl)
      Fakult *= Zaehler++;


Genauso wie if-Anweisungen lassen sich while-Schleifen schachteln. Auch hierbei hilft das Einrücken der Anweisungen innerhalb der Schleife, um den Überblick über die jeweiligen Schleifenenden zu haben. Im folgenden Beispiel wird um die Schleife der Fakultätsberechnung eine weitere Schleife herumgelegt.

Beispiel:

kap06_07.c

01 /*******************************************
02  * Dieses Programm berechnet die Fakultaet
03  * einer eingegebenen Zahl und gibt das
04  * Ergebnis auf dem Bildschirm aus.
05  * Eingabe: Zahl
06  * Ausgabe: Fakult
07  *******************************************/
08 #include <stdio.h>
09
10 int main()
11 {
12    unsigned int Zahl, Zaehler;
13    unsigned long Fakult;
14    char c = 'j';
15
16    printf("Dieses Programm berechnet die ");
17    printf("Fakultaet einer ganzen Zahl.\n");
18    while (c == 'j' || c == 'J')
19    {
20       printf("Geben Sie bitte eine ganze Zahl ein: ");
21       scanf("%d", &Zahl);
22
23       Zaehler = 1;
24       Fakult = 1;
25       while (Zaehler <= Zahl)
26          Fakult *= Zaehler++;
27       printf("%u! = %lu\n", Zahl, Fakult);
28       printf("Moechten Sie eine weitere Zahl ");
29       printf("berechnen? (j/n) ");
30       c = 0;
31       while (c != 'j' && c != 'J' && c != 'n' && c != 'N')
32          scanf("%c", &c);
33    }
34
35    return 0;
36 }

Im Gegensatz zum vorigen Beispielprogramm ist zu beachten, dass es jetzt nicht mehr ausreicht, die Variablen Zaehler und Fakult einmalig mit 1 zu initialisieren. Da die Berechnung mehrmals durchgeführt werden kann, ist es wichtig, diese beiden Variablen direkt vor der Berechnung auf den Startwert zu setzen.

Bei der äußeren while-Schleife ist es von Nachteil, dass die Variable c zuvor mit dem Zeichen 'j' initialisiert sein muss. Dies ist bei der do-while-Schleife anders.

6.6. Schleifen: do-while-Schleifen

Bei der do-while-Schleife ist die Abfrage der Bedingung am Ende. Dadurch wird die Anweisung bzw. der Anweisungsblock innerhalb der Schleife auf jeden Fall mindestens einmal ausgeführt (Motto: Erst zuschlagen, dann fragen), während bei der while-Schleife die Anweisung bzw. der Anweisungsblock bei falscher Bedingung überhaupt nicht ausgeführt wird. Die Syntax für die do-while-Schleife sieht folgendermaßen aus:

do
   Anweisung;
while (Bedingung);


Entsprechend die Syntax mit einem Anweisungsblock:

do
{
   Anweisung1;
   Anweisung2;
   Anweisung3;
} while (Bedingung);


Wichtig: Im Gegensatz zur while-Schleife wird hier hinter while (Bedingung); ein Semikolon gesetzt! Im folgenden Beispiel werden zwei der drei while-Schleifen vom vorigen Beispiel durch do-while-Schleifen ersetzt. Dadurch fällt die Initialisierung der Variablen in der Bedingung weg.

Beispiel:

kap06_08.c

01 /*******************************************
02  * Dieses Programm berechnet die Fakultaet
03  * einer eingegebenen Zahl und gibt das
04  * Ergebnis auf dem Bildschirm aus.
05  * Eingabe: Zahl
06  * Ausgabe: Fakult
07  *******************************************/
08 #include <stdio.h>
09
10 int main()
11 {
12    unsigned int Zahl, Zaehler;
13    unsigned long Fakult;
14    char c;
15
16    printf("Dieses Programm berechnet die ");
17    printf("Fakultaet einer ganzen Zahl.\n");
18    do
19    {
20       printf("Geben Sie bitte eine ganze Zahl ein: ");
21       scanf("%d", &Zahl);
22
23       Zaehler = 1;
24       Fakult = 1;
25       while (Zaehler <= Zahl)
26          Fakult *= Zaehler++;
27       printf("%u! = %lu\n", Zahl, Fakult);
28       printf("Moechten Sie eine weitere Zahl ");
29       printf("berechnen? (j/n) ");
30       do
31          scanf("%c", &c);
32       while (c != 'j' && c != 'J' && c != 'n' && c != 'N');
33    } while (c == 'j' || c == 'J');
34
35    return 0;
36 }

6.7. Schleifen: for-Schleifen

Die dritte Schleife ist die for-Schleife und ist eine sogenannte Zählschleife. Der Name kommt daher, dass ursprünglich bei dieser Schleifenform mit Hilfe einer Zahlenvariablen mitgezählt wurde, wie oft die Schleife ausgeführt werden soll. Entsprechend wird in der Bedingung die Zahlenvariable abgefragt, ob die gewünschte Anzahl von Schleifendurchläufen bereits erreicht ist. In C/C++ sieht die for-Schleife etwas anders aus als in anderen Programmiersprachen. Dadurch ist es möglich, auch for-Schleifen ohne Zähler zu konstruieren.

Die Syntax sieht folgendermaßen aus:

for ([Variablen-Initialisierung];[Bedingung];[Veränderung])
   Anweisung;


bzw. mit mehreren Anweisungen in einem Anweisungsblock:

for ([Variablen-Initialisierung];[Bedingung];[Veränderung])
{
   Anweisung1;
   Anweisung2;
   Anweisung3;
}

Das folgende Beispiel gibt alle Buchstaben von 'A' bis 'Z' auf dem Bildschirm aus. In diesem Fall ist die Zählvariable die char-Variable c.

Beispiel:

kap06_09.c

01 /********************************************
02  * Dieses Programm gibt alle Buchstaben von
03  * 'A' bis 'Z' auf dem Bildschirm aus.
04  ********************************************/
05 #include <stdio.h>
06
07 int main()
08 {
09    char c;
10
11    printf("Dieses Programm gibt alle Buchstaben ");
12    printf("von 'A' bis 'Z' aus.\n");
13    for (c = 'A'; c <= 'Z'; c++)
14       printf("%c ", c);
15    printf("\n");
16
17    return 0;
18 }

In der Syntax sind die Variablen-Initialisierung, die Bedingung und die Veränderung jeweils in eckigen Klammern angegeben, d.h. sie sind optional. So kann zum Beispiel die Variablen-Initialisierung entfallen, wenn die Variablen bereits vorher initialisiert wurden. Die Veränderung des Zählers kann entfallen, z.B. wenn er innerhalb der Schleife verändert wird. Auch die Bedingung kann weggelassen werden, allerdings ist dann die Schleife eine Endlosschleife! Im folgenden Beispiel werden Variablen-Initialisierung und Veränderung weggelassen (oder besser gesagt: an andere Stellen umgesetzt). Trotzdem müssen die Semikolons gesetzt werden.

Beispiel:

kap06_10.c

01 /********************************************
02  * Dieses Programm gibt alle Buchstaben von
03  * 'A' bis 'Z' auf dem Bildschirm aus.
04  ********************************************/
05 #include <stdio.h>
06
07 int main()
08 {
09    char c = 'A'; /* Variablen-Initialisierung */
10
11    printf("Dieses Programm gibt alle Buchstaben ");
12    printf("von 'A' bis 'Z' aus.\n");
13    for ( ; c <= 'Z'; )
14       printf("%c ", c++);
15    printf("\n");
16
17    return 0;
18 }

Für Variablen-Initialisierung, Bedingung und Veränderung können aber auch mehrere Einträge gemacht werden. Diese müssen mit Kommata voneinander getrennt werden (siehe Abschnitt Sequenzen am Anfang dieses Kapitels). Werden mehrere Befehle oder Abfragen als Bedingung angegeben, muss darauf geachtet werden, dass nur das Ergebnis des letzten Befehls bzw. der letzten Abfrage für die Bedingung verwendet wird. Im folgenden Beispiel wird eine Berechnung innerhalb einer for-Schleife in mehreren Schritten so weit verkürzt, dass keine Anweisung innerhalb der Schleife mehr übrigbleibt. In diesem Fall muss ein Semikolon (sozusagen eine Leeranweisung) als Anweisung für die for-Schleife gesetzt werden.

Beispiel:

kap06_11.c

01 #include <stdio.h>
02
03 int main()
04 {
05    unsigned long a, b = 1, c = 1;
06    /* a wird in der for-Schleife initialisiert */
07
08    printf("         a |          b |          c\n");
09    printf("-------------------------------------\n");
10    for (a = 1; a < 10; a = a + 1)
11    {
12       b = b * c;
13       c = c + a;
14       printf("%10lu | %10lu | %10lu\n", a, b, c);
15       c = c + 1;
16    }
17    printf("-------------------------------------\n");
18
19    return 0;
20 }

Im ersten Schritt beim Verkürzen der for-Schleife werden die Kurzform-Operatoren eingesetzt. Ferner werden auch die Startwerte der Variablen b und c in der for-Schleife gesetzt.

kap06_12.c

01 #include <stdio.h>
02
03 int main()
04 {
05    unsigned long a, b, c;
06
07    printf("         a |          b |          c\n");
08    printf("-------------------------------------\n");
09    for (a = 1, b = 1, c = 1; a < 10; a++)
10    {
11       b *= c;
12       c += a;
13       printf("%10lu | %10lu | %10lu\n",a, b, c);
14       c++;
15    }
16    printf("-------------------------------------\n");
17
18    return 0;
19 }

Im zweiten Schritt werden die Initialisierungen zu einer zusammengefasst. Außerdem wird das c++ zu den Veränderungen verschoben.

kap06_13.c

01 #include <stdio.h>
02
03 int main()
04 {
05    unsigned long a, b, c;
06
07    printf("         a |          b |          c\n");
08    printf("-------------------------------------\n");
09    for (a = b = c = 1; a < 10; a++, c++)
10    {
11       b *= c;
12       c += a;
13       printf("%10lu | %10lu | %10lu\n",a, b, c);
14    }
15    printf("-------------------------------------\n");
16
17    return 0;
18 }

Im letzten Schritt werden die drei Anweisungen in die Bedingung verschoben. Wichtig ist, dass die ursprüngliche Bedingung die letzte ist. Nun werden aber vor der Abfrage der Bedingung die drei Anweisungen ausgeführt. Der Ablauf ist damit ähnlich wie bei der do-while-Schleife. Deshalb muss die Anzahl der Schleifendurchgänge um eins verringert werden.

kap06_14.c

01 #include <stdio.h>
02
03 int main()
04 {
05    unsigned long a, b, c;
06
07    printf("         a |          b |          c\n");
08    printf("-------------------------------------\n");
09    for (a = b = c = 1; b *= c, c += a, printf("%10lu | %10lu | "
10                            "%10lu\n", a, b, c), a < 9; a++, c++)
11       ;
12    printf("-------------------------------------\n");
13
14    return 0;
15 }

Natürlich ist diese komprimierte Schreibweise nicht sehr leserlich, sehr fehleranfällig und daher auch nicht zu empfehlen!

Genau wie bei den beiden anderen Schleifenformen können auch for-Schleifen beliebig verschachtelt werden. Zur besseren Übersicht sollten auch hier die Anweisungen innerhalb der Schleife eingerückt werden.

6.8. break und continue

Mit den Befehlen break und continue gibt es noch zwei Befehle, mit denen der Ablauf innerhalb von Schleifen beeinflusst werden kann. Der break-Befehl wurde bereits in der switch-Anweisung verwendet, um aus dieser herauszuspringen. Genauso ist es auch bei den Schleifen: Mit Hilfe des break-Befehls wird die Schleife unabhängig von der Bedingung beendet und mit dem nächsten Befehl nach der Schleife fortgefahren. Dies gilt für alle drei Schleifenformen. Als Beispiel dient noch einmal das Programm zur Berechnung der Fakultät.

Beispiel:

kap06_15.c

01 /*******************************************
02  * Dieses Programm berechnet die Fakultaet
03  * einer eingegebenen Zahl und gibt das
04  * Ergebnis auf dem Bildschirm aus.
05  * Eingabe: Zahl
06  * Ausgabe: Fakult
07  *******************************************/
08 #include <stdio.h>
09
10 int main()
11 {
12    unsigned int Zahl, Zaehler;
13    unsigned long Fakult;
14    char c;
15
16    printf("Dieses Programm berechnet die ");
17    printf("Fakultaet einer ganzen Zahl.\n");
18    while (1) /* 1 => Wahr => Endlosschleife! */
19    {
20       printf("Geben Sie bitte eine ganze Zahl ein: ");
21       scanf("%d", &Zahl);
22
23       Zaehler = 1;
24       Fakult = 1;
25       while (Zaehler <= Zahl)
26          Fakult *= Zaehler++;
27       printf("%u! = %lu\n", Zahl, Fakult);
28       printf("Moechten Sie eine weitere Zahl ");
29       printf("berechnen? (j/n) ");
30       do
31          scanf("%c", &c);
32       while (c != 'j' && c != 'J' && c != 'n' && c != 'N');
33       if (c == 'n' || c == 'N')
34          break;    /* hier wird die Schleife verlassen */
35    }
36
37    return 0;
38 }

Der continue-Befehl ist mehr oder weniger das Gegenteil des break-Befehls: Mit ihm wird zum Ende des Schleifenkörpers gesprungen. Bei while- und do-while-Schleifen wird nach continue die Bedingung abgefragt, bei for-Schleifen wird nach continue erst die Veränderung aufgerufen und dann die Bedingung abgefragt.

Als Beispiel wird das Programm zur Berechnung der Fakultät erweitert: Nach der Benutzereingabe der ganzen Zahl wird diese geprüft, ob sie größer oder gleich eins ist. Wenn nicht, wird mit continue die Schleife neu gestartet.

Beispiel:

kap06_16.c

01 /*******************************************
02  * Dieses Programm berechnet die Fakultaet
03  * einer eingegebenen Zahl und gibt das
04  * Ergebnis auf dem Bildschirm aus.
05  * Eingabe: Zahl
06  * Ausgabe: Fakult
07  *******************************************/
08 #include <stdio.h>
09
10 int main()
11 {
12    int Zahl, Zaehler;
13    unsigned long Fakult;
14    char c;
15
16    printf("Dieses Programm berechnet die ");
17    printf("Fakultaet einer ganzen Zahl.\n");
18    while (1) /* 1 => Wahr => Endlosschleife! */
19    {
20       printf("Geben Sie bitte eine ganze Zahl ein: ");
21       scanf("%d", &Zahl);
22       if (Zahl < 1)
23          continue;   /* Schleife von vorne beginnen */
24       Zaehler = 1;
25       Fakult = 1;
26       while (Zaehler <= Zahl)
27          Fakult *= Zaehler++;
28       printf("%u! = %lu\n", Zahl, Fakult);
29       printf("Moechten Sie eine weitere Zahl ");
30       printf("berechnen? (j/n) ");
31       do
32          scanf("%c", &c);
33       while (c != 'j' && c != 'J' && c != 'n' && c != 'N');
34       if (c == 'n' || c == 'N')
35          break;   /* hier wird die Schleife verlassen */
36    }
37
38    return 0;
39 }

6.9. goto

So wie im guten alten Basic gibt es auch in C/C++ den goto-Befehl. Im Normalfall kommt man ohne diesen Befehl aus, aber es gibt ja immer die berühmten Ausnahmen. Generell werden Programme mit goto-Befehlen sehr schnell unübersichtlich!

Die Syntax lautet:

goto Labelname;

Dabei ist Labelname ein beliebiger Name, der irgendwo im Programm (zumindest in der gleichen Funktion!) definiert sein muss. Ein Label wird ganz einfach definiert. Dazu wird der Labelname gefolgt von einem Doppelpunkt vor den Befehl bzw. vor die Anweisung geschrieben, zu der der goto-Befehl springen soll. Damit diese Labels später schneller wiedergefunden werden, sollten sie direkt an den Anfang der Zeile geschrieben werden. Der Befehl dahinter bleibt wie bisher eingerückt.

Als Beispiel wird das vorige Programm genommen. Hier wird der continue-Befehl durch den goto-Befehl ersetzt. Das Label wird vor der ersten Anweisung in der while-Schleife geschrieben.

Beispiel:

kap06_17.c

01 /*******************************************
02  * Dieses Programm berechnet die Fakultaet
03  * einer eingegebenen Zahl und gibt das
04  * Ergebnis auf dem Bildschirm aus.
05  * Eingabe: Zahl
06  * Ausgabe: Fakult
07  *******************************************/
08 #include <stdio.h>
09
10 int main()
11 {
12    int Zahl, Zaehler;
13    unsigned long Fakult;
14    char c;
15
16    printf("Dieses Programm berechnet die ");
17    printf("Fakultaet einer ganzen Zahl.\n");
18    while (1) /* 1 => Wahr => Endlosschleife! */
19    {
20 neu:  printf("Geben Sie bitte eine ganze Zahl ein: ");
21       scanf("%d", &Zahl);
22       if (Zahl < 1)
23          goto neu;   /* Schleife von vorne beginnen */
24       Zaehler = 1;
25       Fakult = 1;
26       while (Zaehler <= Zahl)
27          Fakult *= Zaehler++;
28       printf("%u! = %lu\n", Zahl, Fakult);
29       printf("Moechten Sie eine weitere Zahl ");
30       printf("berechnen? (j/n) ");
31       do
32          scanf("%c", &c);
33       while (c != 'j' && c != 'J' && c != 'n' && c != 'N');
34       if (c == 'n' || c == 'N')
35          break;   /* hier wird die Schleife verlassen */
36    }
37
38    return 0;
39 }

Ein sehr gefährliches Feature der goto-Anweisung ist die Möglichkeit, mitten in Blöcke springen zu können. Dabei wird auch die Initialisierung von Variablen, die am Blockanfang definiert werden, übersprungen (zumindest von den meisten Compilern). Das Ergebnis ist, dass der Inhalt der Variablen nicht vorhersehbar ist.

Beispiel:

kap06_18.c

01 #include <stdio.h>
02
03 int main()
04 {
05    goto Label;
06
07    {
08       int Zahl = 10;
09
10 Label:
11       printf("Zahl = %i\n", Zahl); /* irgendeine Zahl wird ausgegeben! */
12    }
13
14    return 0;
15 }