C++ Tutorial

 Verzweigungen und Schleifen

if-Verzweigung

Die if-Verzweigung wird verwendet, wenn in Abhängigkeit von Bedingungen unterschiedliche Anweisungen auszuführen sind. Sie hat folgende Syntax:

if ([INIT;] AUSDRUCK)
   ANWEISUNG1;  // IF-Zweig
[else
   ANWEISUNG2;]  // ELSE-Zweig

Ergibt die Auswertung von AUSDRUCK true, wird die Anweisung ANWEISUNG1 ausgeführt und ansonsten der optionale else-Zweig oder nichts. Zusätzlich kann innerhalb der if-Klammer ein Ausdruck INIT stehen, der vor der Auswertung von AUSDRUCK ausgeführt wird.

AUSDRUCK kann auch aus mehreren Bedingungen bestehen, die mit den entsprechenden Logikoperatoren (&&, || und !) verknüpft werden.

Standardmäßig folgt auf ein if und else nur eine Anweisung. Um mehrere Anweisungen auszuführen, sind die Anweisungen in einen Block {...} einzuschließen.

// Pruefen ob eingelesener Wert kleiner, gleich oder
// groesser Null ist

#include <iostream>

int main()
{
   // Einzulesender Wert
   short val;
   // Wert einlesen und pruefen ob Zahl positiv
   std::cout << "Geben Sie bitte eine Integer-Zahl ein: ";
   // Alternativ: Zahl einlesen und pruefen in einer Anweisung
   // if (std::cin >> val; val>0)
   std::cin >> val;
   if (val>0)
      std::cout << "Zahl ist positiv.\n";
   else
   {
      // Pruefen ob Null eingegeben wurde
      if (val == 0)
         std::cout << "Sie haben 0 eingegeben.\n";
      else
        // Es wurde eine negative Zahl eingeben
        std::cout << "Zahl ist negativ.\n";
   }
}

Bedingungsoperator und goto

Eine abgewandelte Form der if-Anweisung stellt der Bedingungsoperator dar, der folgende Syntax besitzt:

erg = (AUSDRUCK1) ? AUSDRUCK2 : AUSDRUCK3;

Liefert die Auswertung des Ausdrucks AUSDRUCK1 als Ergebnis true, wird AUSDRUCK2 ausgewertet und das Ergebnis der Variablen erg zugewiesen. Liefert AUSDRUCK1 dagegen false, wird das Ergebnis von AUSDRUCK3 der Variablen erg zugewiesen.

// Bilden des Absolutbetrags von einem int-Wert

#include <iostream>
#include <format>

int main()
{
   // Variablen definieren
   int inVal, outVal;
   // int-Wert einlesen
   std::cout << "Bitte einen Integer-Wert eingeben: ";
   std::cin >> inVal;
   // Absolutbetrag der Eingabe berechnen
   outVal= (inVal<0) ? -inVal : inVal;
   // Eingabe und Absolutbetrag ausgeben
   std::cout << std::format("Eingabe war: {}, Ausgabe ist: {}\n",inVal,outVal);
}

Nur der Vollständigkeit halber sei an dieser Stelle noch erwähnt, dass C++ auch eine goto-Anweisung kennt. Sie wird hier aber nicht weiter betrachtet, da ein 'sauberes' Programm ohne goto auskommt.

constexpr if Anweisung

Mithilfe der constexpr if Anweisung können alternativ vom Compiler zu übersetzende Anweisungen in Abhängigkeit von einer Bedingung defniert werden. D.h. der Compiler erzeugt nur dann den entsprechenden Code, wenn die Bedingung erfüllt ist.

if constexpr ([INIT;] BEDINGUNG)
   ANWEISUNG1;  // if-Zweig
[else
   ANWEISUNG2;] // else-Zweig

Die Bedingung muss ein konstanter Ausdruck sein, der ein boolsches Ergebnis liefert. Sehen wir uns dies an einem Beispiel an. Je nach Wert der Konstanten DEBUG_LEVEL sollen während der Entwicklungsphase einer Anwendung verschiedene Ausgaben zur Verfolgung des Programmablaufs ausgegeben werden.

#include <iostream>

// Debug-Level festlegen
const int DEBUG_LEVEL = 1;

int main()
{
   if constexpr (DEBUG_LEVEL == 1)
   {
      std::cout << "Einfache Debug-Ausgabe.\n";
   }
   if constexpr (DEBUG_LEVEL == 2)
   {
      std::cout << "Detaillierte Debug-Ausgabe!\n";
   }
}

switch-Verzweigung

Sollen je nach Inhalt eines ganzzahligen oder char-Ausdrucks mehrere verschiedene Anweisungen durchlaufen werden, wird hierfür die switch-Verzweigung eingesetzt. Sie hat folgende Syntax:

switch ([INIT;] AUSDRUCK)
{
case K1:
   AKTION1;
   [break;]
   ...
case Kx:
   AKTIONx;
   [break;]
[default:
   AKTIONy;]
}

Der Ablauf der switch-Verzweigung ist folgender:

  • Ist ein optionaler Initialisierungsausdruck INIT vorhanden, wird dieser zunächst ausgewertet.
  • Danach wird der Ausdruck AUSDRUCK ausgewertet. Dieser Ausdruck muss ein ganzzahliges oder char-Ergebnis liefern.
  • Anschließend wird das Ergebnis des Ausdrucks mit den Literalen verglichen, die auf die Schlüsselwörter case folgen. Stimmt das Ergebnis mit einem der aufgeführten Literale überein, so werden die Anweisungen nach der entsprechenden case-Anweisung bis zur optionalen break-Anweisung ausgeführt.
  • Stimmt das Ergebnis mit keinem der aufgeführten Literale überein, werden die Anweisungen nach der optionalen default-Anweisung oder nichts ausgeführt.

Die break-Anweisung innerhalb eines switch-Blocks bewirkt ein unmittelbares Verlassen des Blocks, d.h. nach dem break wird als nächstes die Anweisung ausgeführt, die nach der geschweiften Klammer zu folgt. Ohne break-Anweisung werden alle nach der Einsprungsmarke (case Kx:) folgenden Anweisungen bis zum Ende des switch-Blocks oder bis zum Erreichen eines break ausführt.

Sollen für verschiedene Werte die gleichen Anweisungen ausgeführt werden, können mehrere case-Zweige unmittelbar aufeinanderfolgen. Die Reihenfolge der case-Anweisungen ist dabei beliebig.

#include <iostream>

int main()
{
   // Variable fuer die Eingabe definieren
   char inVal;
   // Buchstaben einlesen
   // Das Einlesen erfolgt im INIT-Ausdruck der switch-Anweisung
   // Je nach Eingabe wird ein anderer Text ausgegeben, wobei
   // nicht nach Klein-/Grossschreibung unterschieden wird
   std::cout << "Bitte geben Sie einen Buchstaben ein: ";
   switch (std::cin >> inVal; inVal)
   {
   case 'e':
   case 'E':
      std::cout << "Sie haben e oder E eingegeben!\n";
      break;
   case 'a':
   case 'A':
      std::cout << "Sie haben a oder A eingegeben.\n";
      break;
   default:
      std::cout << "Sie haben weder ein a noch ein e eingegeben.\n";
   }
}

for-Schleife

Die for-Schleife wird hauptsächlich eingesetzt, wenn bereits vor dem Eintritt in die Schleife die Anzahl der Schleifendurchläufe bekannt ist. Sie hat folgende Syntax:

for (AUSRUCK1; AUSDRUCK2; AUSDRUCK3)
   ANWEISUNG;

AUSDRUCK1 wird vor dem Eintritt in die Schleife ausgewertet und initialisiert i.d.R. einen Schleifenzähler. AUSDRUCK2 bestimmt das Abbruchkriterium und ist üblicherweise ein vergleichender Ausdruck. Ergibt die Auswertung von AUSDRUCK2 false, wird die Schleife beendet. AUSDRUCK3 wird nach dem Ausführen der Schleifenaktion ANWEISUNG ausgewertet; er inkrementiert bzw. dekrementiert für gewöhnlich den Schleifenzähler. Alle 3 Ausdrücke sind optional. So erzeugt eine for-Schleife ohne Abbruchkriterium eine Endlosschleife.

Die Schleifenaktion ANWEISUNG besteht standardmäßig ebenfalls aus einer Anweisung. Sollen mehrere Anweisungen in der Schleife ausgeführt werden, sind die Anweisungen in einem Block {...} einzuschließen.

// Berechnung der Fakultaet n! in einer for-Schleife

#include <iostream>
#include <format>

int main()
{
   // Einzulesendes Datum
   short val;
   // Wert einlesen, dessen Faktualtaet berechnet werden soll
   std::cout << "Wert eingeben (1...20): ";
   std::cin >> val;
   // Unzulaessige Eingabe abfangen
   if ((val<1) || (val>20))
   {
      std::cout << "Nur Werte zwischen 1...20 zulaessig!\n";
   }
   else
   {
      // Ergebnis vorbelegen
      unsigned long res = 1UL;
      // Fakultaet berechnen 1*2*3..*val;
      for (int index=1; index<=val; index++)
         res *= index;
      // Und Ergebnis ausgeben
      std::cout << std::format("Fakultaet von {} ist {}\n",val,res);
   }
}

while-Schleife

Die while-Schleife wird in erster Linie eingesetzt, wenn sich die Abbruchbedingung für die Schleife erst während eines Schleifendurchlaufs ergibt. Sie hat folgende Syntax:

while (AUSDRUCK)
   ANWEISUNG;

Die Schleife wird solange durchlaufen, wie die Auswertung von AUSDRUCK true ergibt. Und auch hier besteht die Schleifenaktion ANWEISUNG standardmäßig nur aus einer Anweisung. Mehrere Anweisungen sind ebenfalls in einem Block {...} einzuschließen.

// Zahlenraten
// Erraten werden soll eine zufaellige Zahl im Bereich 1..20
// Dabei wird bei jedem Versuch ausgegeben, ob die eingegebene
// Zahl kleiner oder groesser als die gesuchte Zahl ist
// Es sind maximal 4 Versuche erlaubt

#include <iostream>
#include <format>
// Header-Dateien fuer den Zufallszahlengenerator
#include <cstdlib>
#include <ctime>

int main()
{
   // Zufallszahlengenerator initialisieren
   std::srand(std::time(nullptr));
   // Zufallszahl erzeugen und auf den Bereich 1..20 begrenzen
   const unsigned int MAXVAL = 20;
   unsigned int number = rand()%MAXVAL + 1;
   // Merker, ob gesuchte Zahl gefunden wurde
   bool found=false;
   // Anzahl der maximalen Versuche und der aktuelle Versuch
   const unsigned int MAXTRY = 4;
   unsigned int shot = 1;
   std::cout << std::format("Raten Sie eine Zahl zwischen 1...{}\n"
                            "Sie haben dazu maximal {} Versuche\n",
                            MAXVAL,MAXTRY);
   // Rateschleife, wird beendet wenn gesuchte Zahl
   // gefunden wurde (found=true) oder die maximale
   // Anzahl der Rateversuche erreicht ist (MAXTRY>=shot)
   while(!found && (MAXTRY >= shot))
   {
      // Einzulesende Zahl
      unsigned int val;
      // Zahl einlesen
      std::cout << std::format("{}. Versuch: ",shot);
      std::cin >> val;
      // Eingabe pruefen ob zulaessig
      if ((val<1) || (val>MAXVAL))
         std::cout << "Eingabe ausserhalb des erlaubten Wertebereichs!\n";
      else
      {
         // Falls Zahl gefunden, Schleife beenden (found=true)
         if (val == number)
         {
            std::cout << "Sie haben die gesuchte Zahl gefunden!\n";
            found = true;
         }
         else
         {
            // Meldung ausgeben, ob eingegebene Zahl zu klein
            // oder zu gross ist
            std::cout << "Leider nix. Ihre Zahl war zu ";
            if (val < number)
               std::cout << "klein.\n";
            else
               std::cout << "gross.\n";
            // Anzahl der Versuche inkrementieren
            shot++;
         }
      }
   } // Ende while-Schleife
   // Gesuchte Zahl ausgeben falls diese nicht gefunden wurde
   if (!found)
      std::cout << std::format("Die gesuchte Zahl war {}\n",number);
}

do-while-Schleife

Auch diese Schleife wird zumeist verwendet, wenn sich die Abbruchbedingung erst während eines Schleifendurchlaufs ergibt. Sie hat folgende Syntax:

do
   ANWEISUNG;
while (AUSDRUCK);

Auch hier wird die Schleife solange durchlaufen, wie die Auswertung von AUSDRUCK true ergibt und mehrere Anweisungen in der Schleife sind ebenfalls in einem Block {...} einzuschließen.

Der Unterschied zur vorherigen while-Schleife liegt darin, dass die Abbruchbedingung bei der while-Schleife vor dem Eintritt in die Schleife ausgewertet wird, während dies bei der do-while-Schleife erst nach einem Schleifendurchlauf erfolgt.

// Berechnung des gleitenden Mittelwerts von
// einzugebenden int-Daten. Die Eingabe wird
// beendet, wenn eine 0 eingegeben wird.

#include <iostream>
#include <format>

int main()
{
   int sum;         // Summe der eingegebenen Werte
   int count = 0;   // Anzahl der eingegebenen Werte
   bool done;       // Merker fuer Schleifenende
   // Einlese-Schleife
   do
   {
      int val;  // Einzulesender Wert
      std::cout << "Bitte einen Wert eingeben (0=Ende): ";
      std::cin >> val;
      // Fertig, wenn der Wert 0 eingegeben wurde
      done = (val == 0);
      // Wenn nicht fertig, Wert aufaddieren und
      // Anzahl der Werte inkrementieren
      if (!done)
      {
         sum += val;
         count++;
      }
   } while(!done);
   if (count != 0)
      std::cout << std::format("Mittelwert: {}\n",sum/count);
   else
      std::cout << "Sie haben keine Werte eingegeben!\n";
}

break und continue Anweisung

Sehen wir uns noch die restlichen zwei Anweisungen an, die bei Schleifen zum Einsatz kommen können.

Die break-Anweisung darf nur innerhalb einer Schleife oder eines case-Zweigs in der switch-Verzweigung stehen. Sie bewirkt das sofortige Verlassen der Schleife bzw. der switch-Verzweigung.

Die continue-Anweisung ist nur innerhalb einer Schleife (for, while, do-while) gültig. Sie bewirkt, dass die restlichen, auf die continue-Anweisung folgenden Anweisungen, übersprungen werden. Die Schleife selbst wird aber nicht beendet. Allerdings sollte ein gut strukturiertes Programm ohne break- oder continue-Anweisungen auskommen, mit Ausnahme des break in der switch-Verzweigung.