C-Tutorial (C oder C++, Vorwort, Installation, Kapitel 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)

Ternärer Operator (?:), switch-Anweisung

7.1. Ternärer Operator ?:

An dieser Stelle möchte ich auf den recht wenig gebräuchlichen Operator ?: eingehen. Da ?: drei Operanden besitzt, bezeichnet man ihn als ternären (dreiteiligen) Operator. Mit ?: lassen sich einige if-Anweisungen sehr kurz und kompakt anschreiben. Das gilt besonders für bedingte Zuweisungen.

Nehmen wir folgendes fiktives Beispiel her: Gibt der Benutzer 1, so wird der Variable a der Wert 1 (gedacht als logisch 1 für wahr) zugewiesen, gibt der Benutzer irgendetwas anderes als 1 ein, soll a der Wert 0 (logisch 0 für falsch) zugewiesen werden. Zunächst die allgemeine Syntax:

Bedingung ? Rückgabewert wenn wahr : Rückgabewert wenn falsch

Das lässt sich am besten mit einem Beispiel zeigen:

#include <stdio.h>
 
int main()
{
  int eingabe, ergebnis;
  printf ("Werteingabe: ");
  scanf ("%d",&eingabe);
 
    /* ist die Bedingung erfüllt, speichert ergebnis 1, ansonsten 0 */
  ergebnis = (eingabe == 1) ? 1 : 0;
 
  printf ("Wert von ergebnis: %d\n", ergebnis);
  return 0;
}

In die Variable eingabe wird zunächst ein Wert eingelesen. Dann wird mit der Bedingung (eingabe == 1) überprüft, ob dieser 1 war. Sie sollten hier Klammern setzen, um die Übersichtlichkeit zu erhöhen und eine mögliche Fehlerquelle (Operatoren-Priorität) gleich von vornherein auszuschließen. Ist der Ausdruck wahr, d.h. enthält die Variable eingabe den Wert 1, dann wird der Wert 1 zurückgeliefert. Was zurückgeliefert werden soll, folgt hinter dem Fragezeichen (?). Hier hätte ich auch jeden x-beliebigen anderen Wert wählen können. Ist die Bedingung nicht erfüllt, wird zurückgeliefert, was hinter dem Doppelpunkt (:) steht. In diesem Fall ist das 0. Aber auch hier wäre jeder andere Wert möglich. Ein weiteres Beispiel:

#include <stdio.h>
 
int main()
{
  int eingabe;
  printf ("Geben Sie einen Wert < 100 ein: ");
  scanf ("%d",&eingabe);
 
  printf ("War die Eingabe richtig? %s\n", ((eingabe < 100) ? "Ja" : "Nein") );
 
  return 0;
}

Neu ist hier das Formatelement %s in printf(), das einen String (Zeichenkette) ausgibt. Wenn eingabe kleiner als 100 ist, wird Ja zurückgeliefert (der Wert nach dem Fragezeichen ?), anderenfalls Nein (Wert nach dem Doppelpunkt :). Ich habe hier den kompletten Ausdruck geklammert. Das ist nicht notwendig, zeigt aber eindeutig, was zusammen gehört.

7.2. switch-Anweisung

Sie stehen vor folgendem Problem: Der Benutzer soll eine Eingabe tätigen. Je nach Eingabe soll etwas anderes getan werden. Das kennen Sie von älteren Programmen, wo die Programmsteuerung über Buchstaben erfolgt (Drücken Sie e, um das Programm zu beenden ...). Aber auch in heutigen Programmen, wo etwa Kommandozeilenparameter angegeben werden können (z.B. -E und -o kennen Sie bereits als GCC-Parameter), kommt diese Art der Steuerung vor.

Nun gilt es, mehrfache Unterscheidungen zu treffen. Sie müssen mehrmals überprüfen, ob eine - unterschiedliche - Bedingung zutrifft. Nehmen wir als (Folge-)Aktion einfach die Ausgabe eines Textes her. Je nach Eingabe des Benutzers wird ein anderer Text ausgegeben. So einfach, so sinnlos.

Die Aufgabenstellung können Sie bereits mit if-Anweisungen lösen. Wenn der Eingabewert auf drei (hier: 1, 2 oder 3) verschiedene Werte überprüft werden soll, sähe das z.B. wie folgt aus:

#include <stdio.h>
 
int main()
{
  int eingabe;
  printf ("Geben Sie einen Wert ein: ");
  scanf ("%d", &eingabe);
 
  if (eingabe == 1) printf ("Sie haben eins eingegeben.\n");
  if (eingabe == 2) printf ("Die Eingabe war zwei.\n");
  if (eingabe == 3) printf ("Drei wurde eingegeben.\n");
 
  return 0;
}

Nun fehlt noch der Fall etwas anderes, also wenn der Wert kleiner als 1 oder größer als 3 ist. Dafür gibt es zwei Möglichkeiten. 1. Möglichkeit, eine weitere if-Anweisung:

#include <stdio.h>
 
int main()
{
  int eingabe;
  printf ("Geben Sie einen Wert ein: ");
  scanf ("%d",&eingabe);
 
  if (eingabe == 1) printf ("Sie haben eins eingegeben.\n");
  if (eingabe == 2) printf ("Die Eingabe war zwei.\n");
  if (eingabe == 3) printf ("Drei wurde eingegeben.\n");
 
  if ( (eingabe != 1) && (eingabe != 2) && (eingabe != 3) )
    printf ("Es wurde etwas anderes eingegeben.\n");
 
  return 0;
}

Nun wird Es wurde etwas anderes eingegeben. ausgegeben, falls eingabe ungleich 1 UND ungleich 2 UND ungleich 3 ist. Es gibt aber noch Möglichkeit 2. Die zweite Variante bezeichnet man häufig als else-if-Konstrukt. Sehen Sie sich das Beispiel einmal an:

#include <stdio.h>
 
int main()
{
  int eingabe;
  printf ("Geben Sie einen Wert ein: ");
  scanf ("%d",&eingabe);
 
  if (eingabe == 1) printf ("Sie haben eins eingegeben.\n");
  else if (eingabe == 2) printf ("Die Eingabe war zwei.\n");
  else if (eingabe == 3) printf ("Drei wurde eingegeben.\n");
  else printf ("Es wurde etwas anderes angegeben.\n");
 
  return 0;
}

Ganz wichtig dabei: Hier gibt es KEINE neuen Sprachelemente! Das Beispiel besteht aus der "einfachsten" Form der if-else-Anweisung. Damit meine ich lediglich, dass keine Anweisungsblöcke nötig waren, sondern nur jeweils eine Programmierzeile als Anweisung (mit { } geht es natürlich genauso!).

Hier ist leider nicht auf den ersten Blick erkennbar, was zusammen gehört. Ich versuche einmal, es anders zu formatieren:

#include <stdio.h>
 
int main()
{
  int eingabe;
  printf ("Geben Sie einen Wert ein: ");
  scanf ("%d",&eingabe);
 
  if (eingabe == 1) printf ("Sie haben eins eingegeben.\n");
  else
     if (eingabe == 2) printf ("Die Eingabe war zwei.\n");
     else
        if (eingabe == 3) printf ("Drei wurde eingegeben.\n");
        else printf ("Es wurde etwas anderes angegeben.\n");
 
  return 0;
}

Die erste if-Zeile dürfte klar sein. Diese besteht aus einer printf()-Anweisung. Die Zeile endet mit dem Semikolon ;. Der else-Block besteht für den Compiler ebenfalls aus nur einer "Zeile". Das liegt daran, dass das komplette if-else-Konstrukt als eine Zeile angesehen wird! Die if-Zeile müsste klar sein. Im else-Block kommt hier aber ein weiteres if-else-Konstrukt vor. Das lässt sich - theoretisch - beliebig tief verschachteln. Es handelt sich also um eine verschachtelte if-else-Anweisung. Mit Anweisungsblöcken lässt es sich möglicherweise leichter erkennen:

#include <stdio.h>
 
int main()
{
  int eingabe;
  printf ("Geben Sie einen Wert ein: ");
  scanf ("%d",&eingabe);
 
  if (eingabe == 1) printf ("Sie haben eins eingegeben.\n");
  else
  {
     if (eingabe == 2) printf ("Die Eingabe war zwei.\n");
     else
     {
        if (eingabe == 3) printf ("Drei wurde eingegeben.\n");
        else printf ("Es wurde etwas anderes angegeben.\n");
     }
  }
 
  return 0;
}

Der Grund, warum ich das hier erwähne, ist der: In manchen anderen Programmiersprachen, wie z.B. PHP oder Perl, gibt es dafür ein eigenes Sprachkonstrukt. Das sind in der Regel ein oder mehrere "elseif"- (PHP) oder "elsif"-Zweige (Perl), die vor dem else-Zweig vorkommen dürfen. Da es in C so etwas nicht gibt, behilft man sich mit dem eben vorgestellten Konstrukt.

Aber nun zum eigenlichen Thema. In manchen Fällen geht das nämlich einfacher: Mit der switch-Anweisung!

#include <stdio.h>
 
int main()
{
  int eingabe;
  printf ("Geben Sie einen Wert ein: ");
  scanf ("%d",&eingabe);
 
  switch (eingabe)
  {
    case 1:  printf ("Sie haben eins eingegeben.");
             break;
 
    case 2:  printf ("Die Eingabe war zwei.");
             break;
 
    case 3:  printf ("Drei wurde eingegeben.");
             break;
 
    default: printf ("Es wurde etwas anderes angegeben.");
  }
 
  return 0;
}

Mit der switch-Anweisung lassen sich Verzweigungen bilden. Die switch-Anweisung hat Ähnlichkeit mit if, und hat folgende allgemeine Syntax:

switch (Ausdruck)
{
  case Wert1: Anweisung1;
              Anweisung2; 
              ...
              break;

  case Wert2: Anweisung1;
              Anweisung2; 
              ...
              break;
  ...
  ...
  ...
 
  [ default: Anweisungen; ]
}

Nach switch wird in Klammern die Variable (oder Funktion, die einen Rückgabewert liefert) angegeben, deren Wert überprüft werden soll. Je nachdem, welchen Wert sie enthält, sollen unterschiedliche Dinge getan werden. Was, das steht im entsprechenden case-Zweig.

Der Wert nach case ist quasi die Bedingung. Aber nur quasi. Hier gibt es einen entscheidenden Unterschied zur if-Anweisung: Der Wert muss konstant sein! Der Wert muss zur Zeit des Compilierens bereits fest stehen, darf sich also nicht erst während des Programmablaufs ergeben! Das heißt, im Gegensatz zu if sind hier keine "komplizierten" Bedingungen möglich. Der Ausdruck nach switch, also im Normalfall die Variable, die überprüft werden soll, muss erst zur Laufzeit feststehen. Sonst hätte das ja keinen Sinn. ;-) Variablen können sich ändern.

Die Variable wird nun überprüft. Die case-Zweige werden von oben nach unten überprüft. Und zwar solange, bis der case-Zweig gefunden wurde, dessen Konstante dem Wert der Variable entspricht. Die Anweisungen dieses case-Zweigs werden ausgeführt.

Hier beginnt dann die Besonderheit von C, und vielen Sprachen, auf die C Einfluss hatte. Es werden ALLE darauf folgenden Anweisungen ausgeführt, BIS ein break folgt oder die switch-Anweisung zu Ende ist. Einen Anweisungsblock mit { und } brauchen Sie bei switch nicht. Das nächste Beispiel demonstriert, wie wichtig die break-Anweisungen sind:

#include <stdio.h>
 
int main()
{
  int wert = 0;
 
  printf ("Geben Sie einen Wert ein: ");
  scanf ("%d",&wert);
 
    /* ACHTUNG: Hier fehlen absichtlich die break-Anweisungen! */
 
  switch (wert)
  {
    case 1:  printf ("Eins lautet der Wert.\n");
    case 2:  printf ("Zwei.\n");
    case 3:  printf ("Drei ist der Wert!\n");
    default: printf ("Es wurde etwas anderes eingegeben.\n");
  }
 
  return 0;
}

Testen Sie das Beispiel selbst mit Eingaben von 1 bis 3, darüber und darunter. Ab dem passenden case-Zweige werden dann alle printf()-Anweisungen ausgeführt! Für 1 sehen Sie vier Bildschirmausgaben, für 3 zwei printf()-Ausgaben und für alles darüber nur eine.

Der Zweck des default-Zweiges ist offensichtlich: Er ist das Gegenstück des else-Zweigs der if-Anweisung. Er wird dann angesprungen, wenn sonst nichts passt, also für alles andere. Lassen Sie den default-Zweig weg, passiert bei keinem passenden case-Zweig übrigens einfach gar nichts. Die Programmausführung geht dann - darunter - normal weiter. Der default-Zweig ist also optional, was in der Syntax-Darstellung (s.o.) die eckigen Klammern [ ] andeuten sollen.

Steht default ganz am Ende der switch-Anweisung, ist kein break nötig.

Vorheriges Kapitel Nächstes Kapitel