C++-Programmierung: Kontrollstrukturen

Aus Wikibooks
Zur Navigation springen Zur Suche springen
Symbol opinion vote.svg
Alte Seite
Diese Seite gehört zum alten Teil des Buches und wird nicht mehr gewartet. Die Inhalte sollen in das neue Buch einfließen: C++-Programmierung/ Inhaltsverzeichnis.
Eine aktuelle Seite zum gleichen Thema ist unter C++-Programmierung/ Einführung in C++/ Verzweigungen, C++-Programmierung/ Einführung in C++/ Schleifen und C++-Programmierung/ Einführung in C++/ Auswahl verfügbar.

Kontrollstrukturen dienen dazu, den Programmablauf zu kontrollieren. Es gibt die Möglichkeit, Programmteile nur unter gewissen Bedingungen auszuführen oder Programmteile zu wiederholen.

Verzweigung[Bearbeiten]

Eine Verzweigung (bedingte Anweisung, conditional statement) dient dazu, ein Programm in mehrere Pfade aufzuteilen. Beispielsweise kann so auf Eingaben des Benutzers reagiert werden. Je nachdem, was der Benutzer eingibt, ändert sich der Programmablauf.

if[Bearbeiten]

Verzweigungen werden mit dem Schlüsselwort if begonnen. In der einfachsten Form sieht das so aus:

if (bedingung)
  anweisung;

Wenn die bedingung erfüllt ist, wird die anweisung ausgeführt, ansonsten wird sie übersprungen. Sollen nicht nur eine, sondern mehrere Anweisungen ausgeführt werden, fassen Sie diese mit {} zu einem Block zusammen:

if (bedingung)
{
  //Anweisungen
  anweisung1;
  anweisung2;
  //...
}

Als bedingung darf ein beliebiger Ausdruck eines arithmetischen Typs (z.B. bool, int oder float) stehen. Die Bedingung gilt genau dann als erfüllt, wenn der Wert dieses Ausdruck ungleich null ist.

Beispiel:

int i;
cin >> i; 
if (i == 10) 
{
  cout << "Der Benutzer hat zehn eingegeben" << flush;
}

Vergleichsoperatoren[Bearbeiten]

Im obigen Beispiel kam schon der Vergleichsoperator == zum Einsatz. In C++ gibt es insgesamt sechs Vergleichsoperatoren. Sie liefern jeweils den Wert true, wenn die beiden Operanden (die links und rechts des Operators stehen) dem Vergleichskriterium genügen, ansonsten den Wert false.

== identisch
<= ist kleiner (oder) gleich
>= ist größer (oder) gleich
< ist kleiner
> ist größer
!= ist ungleich

else[Bearbeiten]

Das Schlüsselwort else erweitert die Einsatzmöglichkeiten der Verzweigung. Der Code nach else wird nur ausgeführt, wenn die Bedingung falsch ist. Sie können in einer Verzweigungsanweisung auch mehr als zwei Alternativen angeben:

int i;
cin >> i; 
if (i == 10) 
{
  cout << "Der Benutzer hat zehn eingegeben" << flush;
}
else if (i == 11)
{
  cout << "Der Benutzer hat elf eingegeben" << flush;
}
else
{
  cout << "Der Benutzer hat weder zehn noch elf eingegeben" << flush;
}

Es können beliebig viele Zweige mit else if vorkommen. Ob man die einleitende Klammer { des Anweisungsblocks in eine eigene Zeile oder direkt hinter die Bedingung schreiben soll, ist Geschmackssache; in der Literatur werden Sie beiden Varianten begegnen.

Logische Operatoren[Bearbeiten]

Mit logischen Operatoren können Sie mehrere Bedingungen zu einem Ausdruck verknüpfen. C++ bietet folgende Möglichkeiten:

! NICHT Resultat wahr, wenn der Operand falsch ist
&& UND Resultat wahr, wenn beide Operanden wahr sind
|| ODER Resultat wahr, wenn mindestens ein Operand wahr ist (inclusive-or)

Beispiel:

int i = 10, j = 20;
if ((i == 10) && (j == 10))
{
  cout << "Beide Werte sind gleich zehn" << flush;
}
if ((i == 10) || (j == 10))
{
  cout << "Ein Wert oder beide Werte sind gleich zehn" << flush;
}

Bei den Operatoren UND und ODER werden die Teilausdrücke von links nach rechts bewertet, und zwar nur so lange, bis das Resultat feststeht. Wenn z.B. bei einer UND-Verknüpfung schon die erste Bedingung falsch ist, wird die zweite nicht mehr untersucht.

Die Operatoren lassen sich übersichtlich mit Wahrheitstafeln beschreiben:

NICHT
a 1 0
!a 0 1
UND
a 1 1 0 0
b 1 0 1 0
a && b 1 0 0 0
ODER
a 1 1 0 0
b 1 0 1 0
a || b 1 1 1 0

Bedingter Ausdruck[Bearbeiten]

Häufig werden Verzweigungen eingesetzt, um abhängig vom Wert eines Ausdrucks eine Zuweisung vorzunehmen. Das können Sie mit dem Operator ? auch einfacher formulieren wie in diesem Beispiel dargestellt:

min = (a < b)? a : b;

Der Variablen min wird der kleinere der beiden Werte a und b zugewiesen. Analog zum Verhalten der logischen Operatoren wird nur derjenige „Zweig“ bewertet, der das Resultat tatsächlich beeinflusst.

Die Syntax hierzu ist:

Wert = Abgefragte Bedingung ("Frage") ? Rückgebewert für WAHR : Rückgabewert für FALSCH ;

Im obigen Beispiel also: Ist a kleiner als b? Wenn ja (d.h. a kleiner), dann a zuweisen, wenn nein (d.h. b kleiner), dann b zuweisen.

Schleifen[Bearbeiten]

Eine Schleife (loop) ist eine Kontrollstruktur, die einen Teil des Codes solange wiederholt, bis eine Abbruchbedingung eintritt. Dabei wird zwischen Schleifen mit einer Prüfung der Abbruchbedingung zu Beginn der Schleife (abweisend) und solchen mit einer Prüfung nach Abarbeiten des Schleifenrumpfs (nichtabweisend) unterschieden.

for-Schleife[Bearbeiten]

Im Kopf einer for-Schleife stehen drei Ausdrücke (beachten Sie, dass auch eine Zuweisung als Ausdruck gilt):

for (start; bedingung; weiter)
{
  //Anweisungen
}

start: Wird zu Beginn der Schleife bewertet. Beispielsweise können Sie hier eine Zählvariable initialisieren.

bedingung: Solange diese Bedingung wahr ist, wird der Schleifenrumpf ausgeführt. Sie wird einmal direkt nach start geprüft und dann wieder nach jedem Durchlauf. Sobald die Bedingung falsch ist, wird die Schleife „abgebrochen“, d.h. das Programm hinter dem Schleifenrumpf fortgesetzt.

weiter: Jedesmal wenn der Schleifenrumpf abgearbeitet wurde, wird dieser Ausdruck bewertet. So kann beispielsweise die Zählvariable geändert werden.

Jeder der drei Ausdrücke darf weggelassen werden, die Semikolons bleiben als Trennzeichen bestehen.

Beispiel mit allen Angaben:

#include <iostream>

using namespace std;

int main()
{
  for (int x=1; x<=10; x++)
  {
    cout << x << endl;
  }
}

Dieses Beispiel schreibt die Zahlen von 1 bis 10 in die Konsole. Wird eine Variable im Startausdruck definiert - im Beispiel ist es die Variable x - so ist sie eine lokale Variable der Schleife. Der Inkrement-Operator ++ erhöht den Wert seines Operanden um 1.

Beispiel mit nur zwei Angaben:

#include <iostream>

using namespace std;

int main()
{
  int x;
  cin >> x; 
  for (; x; x--)
  {
    cout << x << endl;
  }
}

In der Konsole wird von der eingegebenen Zahl aus rückwärts gezählt. Überlegen Sie was passiert, wenn der Benutzer 0 oder eine negative Zahl eingibt!

while-Schleife[Bearbeiten]

Die for-Schleife ist ein sehr flexibles Sprachkonstrukt. Theoretisch könnte man in C++ alle Schleifen auf diese Weise formulieren. Aber um die Programmierung zu vereinfachen und die Programme besser lesbar zu machen, gibt es in C++ noch zwei weitere Schleifenarten. Die while-Schleife hat eine einfache Syntax und lediglich eine Schleifenbedingung.

while (bedingung)
{
  //Anweisungen
}

Solange die Bedingung wahr ist, wird der Anweisungsblock in den geschweiften Klammern ausgeführt. Sobald die Bedingung falsch ist, fährt das Programm nach der while-Schleife fort.

#include <iostream>

using namespace std;

int main()
{
  int x=1;
  while (x!=0)
  {
    cout << "Geben sie 0 ein, um das Programm zu beenden" << endl;
    cin >> x;
  }
}

Das Programm wird hier erst beendet, wenn der Benutzer die Zahl 0 eingibt.

do-Schleife[Bearbeiten]

Die Unterschiede zwischen der while-Schleife und der do-Schleife sind, dass die do-Schleife mindestens einmal durchlaufen wird und die Abbruchbedingung erst am Schluss der Schleife steht.

do
{
  //Anweisungen
}
while (bedingung);

Mit der do-Schleife können wir das obige Beispiel vereinfachen:

#include <iostream>

using namespace std;

int main()
{
  int x;
  do
  {
    cout << "Geben sie 0 ein, um das Programm zu beenden" << endl;
    cin >> x;
  } while (x!=0);
}

break und continue[Bearbeiten]

Wenn Schleifen komplizierte Aufgaben erledigen sollen, helfen diese beiden Anweisungen, das Programm klarer zu formulieren. break bewirkt, dass die Schleife sofort abgebrochen wird. Bei continue hingegen wird der Rest des Schleifenrumpfs übersprungen und gleich der nächste Durchlauf begonnen.

#include <iostream>

using namespace std;

int main()
{
  for (int x=1; x<20; x++)
  {
    if (x==10)
      continue;
    cout << x << endl;
  }
}

Bei diesem Beispiel wird in der Konsole die Zahl 10 fehlen. Kleiner Tipp zum besseren Verständnis: Die continue-Anweisung sorgt dafür, dass zunächst die Weiter-Anweisung der for-Schleife ausgeführt - also der Zähler erhöht - und anschließend die Bedingung geprüft wird. Die Startanweisung der Schleife wird nicht noch einmal ausgeführt!

Fallunterscheidung[Bearbeiten]

switch[Bearbeiten]

Die switch-Anweisung dient dazu, mehrere verschiedene Fälle zu unterscheiden, ohne längliche else if-Ketten verwenden zu müssen. Folgende Syntax liegt ihr zugrunde:

switch (ausdruck)
{
  case ''marke1'': anweisungen1;
  case ''marke2'': anweisungen2;
  //...
  default: anweisungen_sonst;
}

Innerhalb des switch-Blocks können verschiedene Sprungmarken gesetzt werden, mit denen der Wert von ausdruck verglichen wird. Als Marken sind nur konstante Werte eines ganzzahligen Datentyps erlaubt, z.B. char oder int! Die Anzahl der Marken ist beliebig, der default-Teil darf auch fehlen. Beispiel:

#include <iostream>

using namespace std;

int main()
{
  char c;
  cin >> c;
  switch(c)
  {
    case 'a':
      cout << "Ausgabe: a" << endl;
    case 'b':
      cout << "Ausgabe: nicht a" << endl;
  }
}

Hier stolpern Sie auch gleich über einen „beliebten“ Fehler. Wenn Sie a eingeben, wird das Programm zwar die Anweisungen hinter der Marke a ausführen, dann aber nicht etwa den switch verlassen, sondern auch die Anweisungen hinter der Marke b abarbeiten! Das heißt, in der Konsole steht dann sowohl "Ausgabe: a", als auch "Ausgabe: nicht a". Wenn b eingegeben wird, erscheint nur "Ausgabe: nicht a". Wenn irgend ein anderer Wert eingegeben wird, erscheint nichts.

break[Bearbeiten]

Um zu verhindern, dass das Programm von einem Fall zum nächsten „durchfällt“, haben Sie das Schlüsselwort break zur Verfügung. Nach einem break werden alle weiteren Anweisungen im switch ignoriert, und das Programm fährt nach Ende des Anweisungsblocks fort.

#include <iostream>

using namespace std;

int main()
{
  char c;
  cin >> c;
  switch(c)
  {
    case 'a':
      cout << "Ausgabe: a" << endl;
      break; // Sprung an das Ende des switch-Blocks
    case 'b':
      cout << "Ausgabe: b" << endl;
      break;
  }
}

default[Bearbeiten]

Im obigen Beispiel wird eine Eingabe, welche weder den Wert a noch b besitzt, ignoriert. Um auch in diesem Fall etwas ausgeben zu können, gibt es das Schlüsselwort default. Dieses wird nur angesprungen, wenn kein anderer Fall eintritt.

#include <iostream>

using namespace std;

int main()
{
  char c;
  cin >> c;
  switch(c)
  {
    case 'a':
      cout << "Ausgabe: a" << endl;
      break;
    case 'b':
      cout << "Ausgabe: b" << endl;
      break;
    default:
      cout << "Weder noch" << endl;
      break;
  }
}

Sprunganweisung[Bearbeiten]

Mit der Sprunganweisung goto Labelname; kann die Ausführung des Programms an einer anderen Stelle, bei der angegebenen Marke (label), fortgesetzt werden. Ein Label wird mit einem beliebigen Namen

Labelname:

definiert und nicht mit einem Semikolon abgeschlossen (auch wenn ein Semikolon zu viel nicht stören würde). Es kann nur innerhalb einer Funktion gesprungen werden.

Mit Hilfe von goto lässt sich der Quelltext beliebig unübersichtlich gestalten (so genannter „Spaghetti-Code“), deshalb ist diese Anweisung verpönt. Wenn Sie in Versuchung geraten, goto einzusetzen, überdenken Sie ihren Ansatz! Es gibt praktisch immer eine bessere Lösung (z.B. Schleifenkonstrukte oder Ausnahmebehandlung).