Kapitel 3 - Iterationen

Beispiel 3.1: Ausgabe von Kubikzahlen (while)
Beispiel 3.2: Summe der Quadrate (while)
Beispiel 3.3: Funktion zur Berechnung der Fakultät (do...while)
Beispiel 3.4: Noch einmal die Summe der Quadrate (for)
Beispiel 3.5: Noch einmal die Funktion zur Berechnung der Fakultät (for)
Beispiel 3.6: Extremwerte in einer Zahlenfolge (for)
Beispiel 3.7: Mehrere Laufvariablen (for)
Beispiel 3.8: Aus einer Endlosschleife ausbrechen (while)
Beispiel 3.9: Eingabesteuerung über einen Abbruchwert ( for / break)
Beispiel 3.10: Einsatz der continue- und der break-Anweisung
Beispiel 3.11: Verlassen von verschachtelten Schleifen (goto)
Beispiel 3.12: Übermässige Verwendung von goto-Anweisungen

Eine Iteration ist eine wiederholte Ausführung einer Anweisung oder eines Anweisungsblockes in einem Programm.

C++ verfügt über drei Iterationsanweisungen: Iterationsanweisungen werden aufgrund ihres zyklischen Charakters auch Schleifen genannt.

Weitere Anweisungen:

3.1 Die while-Anweisung



Die while Anweisung hat die Syntax

while (bedingung) anweisung;


Zunächst wird die Bedingung ausgewertet. Wenn das Ergebnis ungleich null (also true) ist, wird die anweisung ausgeführt und die bedingung erneut geprüft. Diese beiden Schritte werden solange wiederholt, bis die Auswertung der bedingung das Ergebnis null (also false) liefert.

Beachten sie, dass die Klammern um die Bedingung herum erforderlich sind.

Beispiel 3.1: Ausgabe von Kubikzahlen

Dieses Programm benutzt die while-Schleife zur Ausgabe von Kubikzahlen:

#include<conio.h>
#include<iostream.h>


int main()
{
int n;
cout << "Geben Sie positive Integerzahlen ein, beenden Sie mit 0.\n\t: ";
cin >> n;

// Beginn der Schleife

while ( n > 0)
{
cout << "Die Kubikzahl von " << n << " ist " << n*n*n << "\n\t: ";
cin >> n;
}

getch();
return 0;
}


Geben Sie positive Integerzahlen ein, beenden Sie mit 0.
      : 2
Die Kubikzahl von 2 ist 8
      : 5
Die Kubikzahl von 5 ist 125
      : 7
Die Kubikzahl von 7 ist 343
      : 0

Der erste Eingabewert für n lautet 2. Die while-Anweisung prüft die Bedingung (n>0). Da die Bedingung wahr ist, werden die beiden Anweisungen in der Schleife ausgeführt.
Die zweite Anweisung liest den Wert 5 in n ein. Am Ende der Schleife wird die Steuerung wieder an die Bedingung (n>0) zurückgegeben. Da diese immer noch wahr ist, werden die beiden Anweisungen innerhalb der Schleife erneut ausgeführt. Jedes Mal, wenn die Programmausführung am Ende der Schleife ankommt, wird die Bedingung erneut geprüft. Nach der dritten Iteration hat n den Wert 0, so dass die Bedingung falsch ist und die Schleife beendet wird.

Die meisten C++ Programmierer rücken alle Anweisungen innerhalb der Schleife ein. Dadurch lässt sich die Programmierlogik besser erkennen.

Beispiel 3.2: Summe der Quadrate

Das Programm benutzt eine while-Schleife zur Berechnung der Summe der quadrate der Ganzzahlen zwischen 1 und n:

#include<conio.h>
#include<iostream.h>


int main()
{
int i = 1, n, sum = 0;

cout << "Geben Sie positive Integerzahlen ein: ";
cin >> n;

// Beginn der Schleife

while ( i <= n)
{
sum += i*i;
i++;
}
cout << "Die Summe der ersten " << n << " Quadrate ist " << sum << endl;

getch();
return 0;

}
Geben Sie positive Integerzahlen ein: 4
Die Summe der ersten 4 Quadrate ist 30
Geben Sie positive Integerzahlen ein: 6
Die Summe der ersten 6 Quadrate ist 91

Der erste Lauf berechnet die Summe der ersten 4 Quadrate:
1 + 4 + 9 + 16 = 30


Der zweite Lauf berechnet die Summe der ersten 6 Quadrate:

1 + 4 + 9 + 16 + 25 + 36 = 91


Wenn mehrere Anweisungen innerhalb einer Schleife ausgeführt werden sollen, müssen die geschweiften Klammern { und } verwendet werden, um die Anweisungen in einer zusammengestzten Anweisung zusammenzufassen.
Beispiel 3.2 illustriert die Standardmöglichkeit zur Formatierung einer zusammengesetzten Anweisung in einer Schleife. Die schliessende Klammer befindet sich in einer eigenen Zeile unter dem "w" des Schlüsselwortes while. Dabei werden alle Anweisungen innerhalb der zusammengesetzten Anweisung eingerückt.

Natürlich kümmert sich der Compiler nicht darum, wie der Quelltext formatiert ist. Er würde auch diese Form akzeptieren:

while ( i<= n ) { sum += i*i; i++ };

Die meisten C++ Programmierer halten aber an das vorgestellte Format für besser lesbar. Einige C-Programmierer setzen auch die öffnende Klammer lieber in eine eigenständige Zeile, direkt unter das "w" des while-Schlüsselwortes.

3.2 Die do...while-Anweisung



Die do...while-Anweisung ist mit der while-anweisung fast identisch. Ihre Syntax lautet:

do anweisung while (bedingung);

Der einzige Unterschied ist der, dass die do...while-Anweisung die anweisung erst einmal ausführt und dann die bedingung prüft. Diese beiden Schritte werden wiederholt, bis die bedingung den Wert null ergibt (also false ist). Eine do...while-Schleife wird - unabhängig vom Wert der bedingung - immer mindestens einmal iteriert, weil die anweisung ausgeführt wird, bevor die bedingung zum ersten Mal geprüft wird.

Beispiel 3.3: Funktion zur Berechnung der Fakultät

#include<conio.h>
#include<iostream.h>


int main()
{

int n, f = 1;
cout << "Geben Sie eine positive Integerzahl ein: ";
cin >> n;
cout << endl << " Die Fakultaet von " << n << " ist : ";

do {
      f *=n;
      n--;
      } while (n>1);

cout << f << endl;

getch();
return 0;

}

Geben Sie eine positive Integerzahl ein: 5
Die Fakultaet von 5 ist 120

Geben Sie eine positive Integerzahl ein: 8
Die Fakultaet von 8 ist 40320

Das Programm initialisiert f mit 1 und multipliziert es mit der Eingabezahl n und allen positiven Ganzahlen, die kleiner als n sind. Daher ist

5! = (5)(4)(3)(2)(1)=120 und 8! = (8)(7)(6)(5)(4)(3)(2)(1)=40320.

3.3 Die for-Anweisung

Schleifen werden über drei verschiedene Komponenten gesteuert: Im Programm aus Beispiel 3.3 ist n die Laufvariable der Schleife, ihre Initialisierung ist cin >> n, ihre Fortsetzungsbedingung ist n > 1, und ihre Aktualisierung ist n--. Wenn diese drei Komponenten einfach sind, lässt sich die Schleife als for-Schleife realisieren, die üblicherweise einfacher als die entsprechenden while und do...while-Schleife ist.

Die Syntax der for-Schleife lautet:

for (initialisierung;  fortsetzungsbedingung;  aktualisierung) anweisung;


Die initialisierung, die fortsetzungsbedingung oder die aktualisierung können leer sein.

Beispiel 3.4: Noch einmal die Summe der Quadrate

#include < conio.h >
#include < iostream.h >


int main()
{
int i = 1, n, sum = 0;

cout << "Geben Sie positive Integerzahlen ein: ";
cin >> n;

// Beginn der Schleife

for ( int i=0; i <= n; i++)

sum += i*i;

cout << "Die Summe der ersten " << n << " Quadrate ist " << sum << endl;

getch();
return 0;

} Hier ist int i = 1 die Initialisierung, die Fortsetzungsbedingung lautet i <= n und die Aktualiserung erfolgt über i++.

Es ist üblich, die Deklaration der Laufvariablen in den Initialisierungsteil der for-Schleife mit aufzunehmen. So wird zum Beispiel die Laufvariable i im obigen Programm im Initialisierungsteil als int deklariert:

int i = 0;


Das ist eine nützliche Eigenschaft von C++. Wenn jedoch die Laufvariable einmal auf diese Art und Weise deklariert wurde, sollte sie nicht in einer späteren Schleife umdeklariert werden, wie dies im folgenden Beispiel der Fall ist:

for ( int i = 0; i < 100; i++)
     sum +=i*i;
for ( int i = 0; i < 100; i++) // FEHLER: i wurde bereits deklariert
     cout << i*i*i;


Dieselbe Laufvariable lässt sich aber durchaus erneut verwenden, sie lässt sich nur nicht erneut deklarieren:

for ( int i = 0; i < 100; i++) // OK
     cout << i*i*i;

Wenn Sie die Wahl zwischen einer for- und einer while- oder einer do...while-Schleife haben, sollten Sie sich in der Regel für die for-Schleife entscheiden. Wie das nächste Beispiel verdeutlicht, lässt sich eine for-Schleife meist leichter verstehen.

Beispiel 3.5: Noch einmal die Funktion zur Berechnung der Fakultät:

#include <conio.h>
#include <iostream.h>


int main()
{
int n, f = 1;

cout << "Geben Sie eine positive Integerzahl ein: ";
cin >> n;

for ( int i = 2; i <= n; i++)
f *=i;
cout << endl << " Die Fakultaet von " << n << " ist : " << f << endl;

getch();
return 0;

}

Das Programm berechnet die Fakultät durch Multiplizieren von 1 mit den Faktoren 2, 3, ..., n-1, n.

Es läuft zwar nicht schneller als die Version mit der while-Schleife, dafür ist der Quelltext aber kürzer.

Beispiel 3.6: Extremwerte in einer Zahlenfolge

#include <conio.h>
#include <iostream.h>


int main()
{
int n, min, max;
cout << "Geben Sie positive Integerzahlen ein, beenden Sie mit 0: ";
cin >> n;

for ( min = max = n; n > 0;) {
       if (n <min) min = n;                  // min und max sind der kleinste bzw. der größte bisher kleinste eingelesene Wert
       else if (n > max) max = n;
       cin >> n; }

cout << "Minimum = " << min << " und Maximum = " << max << endl;

getch();
return 0;
}

Geben Sie positive Integerzahlen ein, beenden Sie mit 0:
5
4
5
4
66
85
1
1000
0
Minimum = 1 und Maximum = 1000

Beachten Sie, dass im Initialiserungsteil min = max = n der for-Schleife zwei Zuweisungen entspricht und dass der Aktualisierungsteil leer ist. Beachten Sie auch die Verwendung des Inlinekommentars, der sich über drei Zeilen erstreckt. Er beschreibt eine Schleifen-Invariante, eine Bedingung zu den Variablen, die bei jeder Iteration der Schleife wahr sein sollte.

Ein Abbruchwert ("Wache") ist ein spezieller Wert der Eingabevariablen, der zum Beenden der Eingabeschleife benutzt wird. Im obigen Beispiel wird der Wert 0 als Abbruchwert benutzt.

Beispiel 3.7: Mehrere Laufvariablen

Dieses Programm zeigt, wie eine for-Schleife mehr als eine Laufvariable verwenden kann:

#include <conio.h>
#include <iostream.h>


int
main()
{

for ( int m = 1, n = 8; m < n; m++, n--)
      cout << " m = " << m << " n = " << n << endl;

getch();
return 0;
}

m = 1 n = 8
m = 2 n = 7
m = 3 n = 6
m = 4 n = 5

Der Initialisierungsteil der for-Schleife deklariert die beiden Laufvariablen m und n und initialisiert m mit 1 und n mit 8. Der Aktualisierungsteil verwendet den Kommaoperator, um zwei Aktualisierungsausdrücke mit einzubinden:

m++ und n--


Die Schleife wird so lange fortgesetzt, wie m < n gilt. Beachten Sie, dass es sich bei dem Komma im Initialierungsteil der for-Scheife nicht um den Kommaoperator handelt: Das Komma kommt dort als Teil der Initialisierungsliste zum Einsatz.

3.4 Die break-Anweisung

Die break-Anweisung kennen Sie bereits seit der switch-Anweisung. Sie wird auch in Schleifen verwendet.

Bei ihrer Ausführung wird die Schleife beendet, so dass die Iteration an dieser Stelle "abgebrochen" wird.


Beispiel 3.8: Aus einer Endlosschleife ausbrechen

Die while-Schleife entspricht der aus Beispiel 3.2

while (1) {
       if ( i > n ) break; // Die Schleife stoppt hier, wenn i > n
       sum +=i*i;
       i++;
}

Solange (i <= n) gilt, wird die Schleife - wie in Beispiel 3.2 - fortgesetzt. Sobald aber ( i > n ) gilt, wird die break-Anweisung ausgeführt und die Schleife sofort beendet.

Beispiel 3.9: Eingabesteuerung über einen Abbruchwert

Das Programm liest eine Folge positiver Integerzahlen ein, wird mit einer 0 beendet und gibt den Mittelwert der eingelesenen Integer-Zahlen aus.

int main()
{
int n, count = 0, sum = 0;
cout << "Geben Sie positive Integer-Zahlen ein, beenden Sie mit 0:\n";

for ( ; ; ; ) {
      cout << "\t" << count + 1 << ": ";
      cin >> n;
      if (n == 0) break;
      ++count;
      sum += n;
}

cout << "Der Durchschnitt der " << n << " Zahlen ist " << float(sum)/count << endl;
return 0;
}

Geben Sie positive Integer-Zahlen ein, beenden Sie mit 0:
1: 7
2: 4
3: 5
4: 2
5: 0
Der Durchschnitt der 4 Zahlen ist 4,5

Bei Eingabe von 0 wird die break-Anweisung ausgeführt, die sofort die for-Schleife beendet und dafür sorgt, dass die abschliessende Ausgabeanweisung ausgeführt wird. Ohne die Verwendung von break an dieser Stelle müsste entweder die Anweisung ++count Teil der Bedingung sein oder count müsste ausserhalb der Schleife dekrementiert werden oder auf -1 initialisiert werden.

Beachten Sie, dass alle drei Steuerkomponenten dieser for-Schleife leer sind:

for ( ; ; ).


Dieses Konstrukt läuft "ewig": Ohne die break-Anweisung würde es sich um eine Endlos-Schleife handeln.

3.5 Die continue-Anweisung

Die break - Anweisung überspringt alle übrigen Anweisungen im Schleifenblock und geht zur nächsten Anweisung hinter der Schleife.

Die continue- Anweisung macht dasselbe, mit dem Unterschied, dass die Schleife nicht beendet, sondern zurück an den Anfang des Schleifenblocks springt, um die nächste Iteration zu beginnen.

Beispiel 3.10: Einsatz der continue- und der break-Anweisung

Dieses Programm veranschaulicht die continue- und die break-Anweisung int main()
{

int n;

for ( ; ; ) {
      cout << " Geben Sie Integer-Zahlen ein : " ;
      cin >> n;
            if ( n%2 == 0) continue;
            if ( n%3 == 0) break;
      cout << "\tFuss der Schleife.\n";
      }
cout << "Ausserhalb der Schleife.\n;
}


Geben Sie Integer-Zahlen ein: 7
      Fuss der Schleife.
Geben Sie Integer-Zahlen ein: 4
Geben Sie Integer-Zahlen ein: 9
      Ausserhalb der Schleife


Wenn n den Wert 7 hat, schlagen beide if-Bedingungen fehl, und die Kontrolle erreicht das Ende der Schleife. Wenn n den Wert 4 hat, ist die erste if-Bedingung wahr ( 4 ist ein Vielfaches von 2), so dass die Kontrolle die übrigen Anweisungen in der Schleife überspringt und wieder direkt an den Anfang der Schleife springt, um mit der nächsten Iteration fortzufahren. Wenn n den Wert 9 hat, ist zwar die erste if-Bedingung falsch ( 9 ist kein Vielfaches von 2 ), aber die zweite if-Bedingung ist wahr (  9 ist ein Vielfaches von 3), so dass die Kontrolle die Schleife verlässt und direkt zur ersten Anweisung springt, die der Schleife folgt.

3.6 Die goto-Anweisung

Die Anweisungen break, continue und switch sorgen dafür, dass der Programmfluss zu einer Stelle verzweigt, die sich vom normalen Ablauf unterscheidet. Das Ziel der Verzweigung wird vom Kontext bestimmt.
  • break geht zum nächsten Kontext ausserhalb der Schleife
  • continue geht zur Fortsetzungsbedingung der Schleife
  • switch geht zur passenden case-Konstanten
Diese drei Anweisungen werden als Sprunganweisungen bezeichnet, weil sie dafür sorgen, dass der Programmfluss über andere Anweisungen "hinwegspringt"

Die goto-Anweisung ist eine andere Art der Sprunganweisung.

Ihre Ziel wird über eine Sprungmarke (label) in einer Anweisung festgelegt.

Eine Sprungmarke (label) ist ein Bezeichner, dem ein Doppelpunkt folgt und der am Anfang einer Anweisung steht. Label arbeiten wie die case-Konstanten in einer switch-Anweisung: Sie geben das Ziel an.

Beispiel 3.11: Verlassen von verschachtelten Schleifen

Das Programm zeigt, wie man aus verschiedenen Schleifen korrekt ausbrechen kann.

int main()
{

int a, b, c;

  cin a >> b >> c;
  for ( int i = 0; i < a; i++) {
      for ( int j = 0; j < b; j++)
            for ( int k = 0; k < c; k++)
                  if (i*j*k) goto esc;
                  else cout << I*j*k) << " ";
                  esc: cout <<endl;
}
return 0;
}


Wenn das goto in der innersten Schleife erreicht wird, springt die Programmieranweisung zur Ausgabeanweisung am Ende der äußersten Schleife.

Es gibt andere Möglichkeiten, um derartiges Verlassen der verschachtelten Schleifen zu erreichen. Eine Möglichkeit wäre das Zurücksetzen der Laufvariablen der Schleife, indem man die if-Anweisung in der k-Schleife folgendermaßen zurücksetzt.

            if (i*j*k > 100) j = k = b + c;
            else cout << I*j*k << " ";


Das führt dazu, dass sowohl die j als auch die k-Schleife beendet werden, weil dessen Fortsetzungsbedingungen j < b und k < c dann falsch sind. Dies ist eine "Hacker-Methode", die die Werte der Laufvariablen j und k künstlich verändert, um als Seiteneffekt (Nebenwirkung) zum gewünschten Ergebnis zu kommen.

Ein weiterer Ansatz ist die Verwendung einer "done-Marke" in den Fortsetzungsbedingungen der for-Schleifen. int done = 0;

for ( int i = 0; i < a && !done; i++) {
            for ( int j = 0; j < b && !done; j++)
                   for ( int k = 0; k < c && !done; k++)

                   if (i*j*k) done = 1;
                   else cout << I*j*k) << " ";

Aber auch das ist eine ziemlich künstliche und umständliche Lösung, goto ist wirklich die beste Möglichkeit zum Beenden verschachtelter Schleifen.

Wie das folgende Beispiel illustriert, lassen sich goto-Anweisungen leicht übermässig verwenden.

Beispiel 3.12: Übermässige Verwendung von goto-Anweisungen

Dieses sinnlose Programm zeigt, wie die Verwendung von goto-Anweisungen zu "Spaghetti-Code" führen kann.

#include <condefs.h>
#include <conio.h>
#include <iostream.h>;



int main()
{

int n;

cout << "Geben Sie für n einen Zahlenwert ein: ";
cin >> n;

s1: cout << "Sie Sind jetzt beim Schritt 1 mit n = " << n << endl;
       --n;
        if (n<2) return 0;

s2: cout << "Sie Sind jetzt beim Schritt 2 mit n = " << n << endl;
       if (n<7) goto s4;

s3: cout << "Sie Sind jetzt beim Schritt 3 mit n = " << n << endl;
       if (n%2==0) goto s1;

s4: cout << "Sie Sind jetzt beim Schritt 4 mit n = " << n << endl;
       n -=2;
       if (n<4) goto s1;
       else goto s3;


getch();
return 0;
}


Geben Sie für n einen Zahlenwert ein: 9
Sie sind jetzt beim Schritt 1 mit n = 9
Sie sind jetzt beim Schritt 2 mit n = 8
Sie sind jetzt beim Schritt 3 mit n = 8
Sie sind jetzt beim Schritt 1 mit n = 8
Sie sind jetzt beim Schritt 2 mit n = 7
Sie sind jetzt beim Schritt 3 mit n = 7
Sie sind jetzt beim Schritt 4 mit n = 7
Sie sind jetzt beim Schritt 1 mit n = 5
Sie sind jetzt beim Schritt 2 mit n = 4
Sie sind jetzt beim Schritt 4 mit n = 4
Sie sind jetzt beim Schritt 3 mit n = 2
Sie sind jetzt beim Schritt 1 mit n = 2


Mit der Verringerung von 9 auf 2 lassen die goto-Anweisungen die Programmkontrolle zwischen den vier Ausgabeanweisungen mit den vier Sprungmarken s1, s2, s3 und s4 hin- und herspringen.

Der unvorsichtige Einsatz von goto-Anweisungen führt zu unstrukturiertem "Spaghetti-Code", in dem sich Fehler nur schwer aufspüren lassen.