Die Switch -Anweisung in C++
Durch eine Switch -Anweisung kann die Ausführung eines Programmes bei einer Sprungmarke fortgesetzt werden. Der Wert eines Ausdrucks bestimmt, bei welcher Sprungmarke die Ausführung fortgesetzt wird. Der Ausdruck muß einen ganzzahligen Wert ergeben. (Dazu zählt auch der Typ "bool". Ein Wert einer Aufzählung oder ein in einen ganzzahligen Wert oder Aufzählungswert wandelbarer Wert kommt auch in Frage.) Die Ausführung wird dann bei der Sprungmarke mit diesem Wert fortgesetzt, falls es eine solche Sprungmarke gibt. (Diese Darstellung ist etwas vereinfacht, weitere Details folgen.)
Das Programmbeispiel "peter.cpp" verwendet die Anweisung "switch". Wenn der Parameter "gruss" den Wert "true" hat, dann wird die Ausführung bei der ersten markierten Anweisung "case true: std::cout << "Hallo, ";" fortgesetzt und beide Ausgaben (von ""Hallo, "" und von ""Peter\n"") erfolgen dann hintereinander. Hat der Parameter "gruss" den Wert "false", dann wird die Ausführung bei der zweiten markierten Anweisung "case false: std::cout << "Peter\n";" fortgesetzt und nur der Text "Peter" und ein Zeilenende wird ausgegeben.
peter.cpp
#include <iostream>
#include <ostream>
void peter( bool const gruss )
{ switch( gruss )
{ case true: std::cout << "Hallo, ";
case false: std::cout << "Peter\n"; }}
int main()
{ peter( true ); peter( false ); }std::cout
Hallo, Peter
Peter
Syntax
Die switch-Anweisung wird durch das Nichtterminalsymbol 〈selection-statement 〉 beschrieben.
- 〈selection-statement 〉 ::=
- "switch" "(" 〈condition 〉 ")" 〈statement 〉.
Innerhalb der Anweisung 〈statement 〉 können dann Anweisungen durch vorangehende Sprungmarken markiert sein.
- 〈labeled-statement 〉 ::=
- "case" 〈constant-expression 〉 ":" 〈statement 〉 |
- "default" ":" 〈statement 〉.
Der Wert des Ausdruck 〈constant-expression 〉 (nach dem Schlüsselwort "case") muß zur Zeit der Übersetzung des Programmes feststehen (er muß „konstant“ sein). Es muß sich um einen ganzzahligen Wert (dazu zählt auch der Typ "bool") oder um einen Wert einen Aufzählungstyps handeln. Der Wert des Ausdrucks 〈condition 〉 braucht erst zur Laufzeit festzustehen (er kann „veränderlich“ sein).
Semantik
Die Ausführung des Programmes wird dann bei der Anweisung 〈statement 〉 fortgesetzt, der eine Sprungmarke mit der 〈constant-expression 〉 direkt vorangeht, wenn deren Wert (nach Wandlung in den beförderten Typ der 〈condition 〉) auch der Wert des Ausdrucks 〈condition 〉 ist. Falls es solch eine Anweisung nicht gibt, dann wird keine weitere Anweisung ausgeführt. Mehrere Sprungmarken mit gleichem Wert (nach Wandlung in den beförderten Typ der 〈condition 〉) sind nicht erlaubt. Obwohl das aus der Syntax hier nicht hervorgeht, darf eine Case -Marke nur innerhalb einer Switch -Anweisung verwendet werden (Das ist in der C++ -Norm zusätzlich zu den Produktionsregeln so festgelegt).
Die syntaktische Kategorie 〈constant-expression 〉 umfaßt nicht einfach „konstante Ausdrücke“, sondern schließt auch bestimmte Operatoren aus. Eine 〈constant-expression 〉 ist nämlich eine 〈conditional-expression 〉‚ womit der Komma- und der Zuweisungsoperator in ihr nicht vorkommen können.
- Einfache Anwendungen
- Was gibt das Programm "switch.cpp" aus?
switch.cpp
#include <iostream>
#include <ostream>void a(){ switch( 0 ){ std::cout << "a"; }}
void b(){ switch( 0 ){ case 0: std::cout << "b"; }}
void c(){ switch( 1 ){ case 0: std::cout << "c"; }}
int main(){ a(); b(); c(); std::cout << '\n'; }
break
In dem Programm "peter.cpp" wird die Anweisung zur Ausgabe von ""Peter\n"" für beide mögliche Werte der Bedingung 〈condition 〉 ausgeführt. Die beiden möglichen Ausführungspfade haben also diese Anweisung gemein. Sollen verschiedene Alternative ausgeführt werden, ohne daß diese sich so überlappen, so kann jeder Anweisungsfolge durch die Anweisung "break;" vor der nächsten Marke abgeschlossen werden. Die Sprunganweisung "break;" bewirkt einen Sprung zum Ende der Switch -Anweisung.
Das folgende Programm gibt eine deutschsprachige Textdarstellung für Wahrheitswerte aus. Die letzte Break -Anweisung ist allerdings überflüssig, sie wird nur verwendet weil dies einheitlicher ist und hilft, Fehler zu vermeiden, falls die Reihenfolge der beiden markierten Anweisungen einmal vertauscht werden sollte.
ausgabe.cpp
#include <iostream>
#include <ostream>
void ausgabe( bool const wert )
{ switch( wert )
{ case true: std::cout << "wahr"; break;
case false: std::cout << "falsch"; break; }}
int main()
{ ausgabe( true ); std::cout << '\n';
ausgabe( false ); std::cout << '\n'; }std::cout
wahr
falsch
default
Die Marke "default" erlaubt es einen Anweisung zu kennzeichnen, bei der die Ausführung fortgesetzt werden soll, wenn keiner der angegebenen Werte mit dem Wert des Ausdrucks 〈condition 〉 übereinstimmt. Es darf nur maximal eine Default-Marke verwendet werden. Das Wort "default" ist (wie auch das Wort "case") kein Bezeichner, sondern ein Schlüsselwort.
Das folgende Programm zeigt die Ausgabe von Farbnamen, wobei die Zahlen 0, 1 und 2 für die Farben Rot, Grün bzw. Blau stehen.
farbausgabe.cpp
#include <iostream>
#include <ostream>
void farbausgabe( int const farbe )
{ switch( farbe )
{ case 0: std::cout << "Rot"; break;
case 1: std::cout << "Gruen"; break;
case 2: std::cout << "Blau"; break;
default: std::cout << "(Fehler: Keine Farbe!)"; break; }}
int main()
{ farbausgabe( 0 ); std::cout << '\n';
farbausgabe( 1 ); std::cout << '\n';
farbausgabe( 3 ); std::cout << '\n';
farbausgabe( 2 ); std::cout << '\n'; }std::cout
Rot
Gruen
(Fehler: Keine Farbe!)
Blau
Die hier verwendete Ausgabefunktion verbindet zwei Aufgaben miteinander: Sie ermittelt eine Textdarstellung einer Farbe und gibt diese aus. Besser ist es, wenn jede Funktion nur eine Aufgabe erledigt. Insbesondere sollte eine Funktion, die etwas berechnet (hier die Textdarstellung), nichts ausgeben, sondern ihr Ergebnis als Wert zurückgeben. Da hier aber die dafür benötigten Sprachmittel noch nicht vorausgesetzt werden, geben die Funktionen in dieser Lektion den ermittelten Text auch gleich aus.
Keine passende Sprungmarke
Falls keine Sprungmarke vorhanden ist, deren Wert (nach eventuellen Wandlungen) dem Wert des Ausdrucks 〈condition 〉 gleicht und auch keine Default -Sprungmarke vorhanden ist, dann wird auch keine Anweisung angesprungen.
keine.cpp
#include <iostream>
#include <ostream>
int main()
{ std::cout << 'A';
switch( 1 ){ case 0: std::cout << 'B'; }
std::cout << '\n'; }std::cout
A
Mehrere direkte aufeinanderfolgende Marken
Bei einer markierten Anweisung "case" 〈constant-expression 〉 ":" 〈statement 〉 kann die Anweisung 〈statement 〉 selber wieder eine markierte Anweisung sein. Das bedeutet, daß mehrere Marken direkt hintereinanderstehen können. Das folgende Beispiel zeigt eine Variante des vorherigen Beispiels für den Fall, daß für eine Farbe das Vorzeichen einer Kennzahl positiv oder negativ sein kann.
farbausgabe1.cpp
#include <iostream>
#include <ostream>
void farbausgabe( int const farbe )
{ switch( farbe )
{ case 0: std::cout << "Rot"; break;
case 1: case -1: std::cout << "Gruen"; break;
case 2: case -2: std::cout << "Blau"; break;
default: std::cout << "(Fehler: Keine Farbe!)"; break; }}
int main()
{ farbausgabe( 0 ); std::cout << '\n';
farbausgabe( 1 ); std::cout << '\n';
farbausgabe( -2 ); std::cout << '\n'; }std::cout
Rot
Gruen
Blau
Mehrere im Programmtext direkt aufeinanderfolgende Marken können nicht durch eine Marke ersetzt werden, die eine Liste der Werte enthält. Beispielsweise kann der Quelltext "case 1: case -1:" nicht zu "case 1, -1:" verkürzt werden.
Aufzählungstypen
Das folgende Beispiel zeigt, wie ein Ausdruck von einem Aufzählungstyp in einer switch -Anweisung verwendet wird.
aufzaehlung.cpp
#include <iostream>
#include <ostream>
enum ampelzustand { ROT, ROTGELB, GRUEN, GELB };
void farbausgabe( ampelzustand const farbe )
{ switch( farbe )
{ case ROT: std::cout << "Rot"; break;
case ROTGELB: std::cout << "Rot-Gelb"; break;
case GRUEN: std::cout << "Gruen"; break;
case GELB: std::cout << "Gelb"; break; }}
int main()
{ farbausgabe( GRUEN ); std::cout << '\n'; }std::cout
Gruen
Eine weitere Sprungmarke "case 1" wäre hier nicht erlaubt, da deren Wert nach Wandlung in den Typ der Bedingung "farbe" dem Wert einer schon vorhandenen Sprungmarke ("ROTGELB") gleichen würde.
Bereiche
Es ist nicht möglich, Bereiche von Werten direkt als case -Marken zu verwenden, also etwa eine case -Marke für Werte von 10 bis 19 zu schreiben. Hierfür müssen alle Werte als case -Marke aufgezählt werden. Wenn das nicht erwünscht ist, bleibt noch die Möglichkeit, vorher den Bereich zu ermitteln, wie in dem folgenden Beispiel, in dem die Funktion "bereiche" für Zahlen von 10 bis 20, für Zahlen von 20 bis 29 und für die Zahlen 30 und 31 jeweils unterschiedliches Verhalten zeigt.
bereiche.cpp
#include <iostream>
#include <ostream>
void bereiche( int const zahl )
{ switch( zahl < 20 ? 19 : zahl < 30 ? 20 : zahl )
{ case 19: std::cout << "Bereich bis 19"; break;
case 20: std::cout << "Bereich von 20 bis 29"; break;
case 30: std::cout << "Zahl 30"; break;
case 31: std::cout << "Zahl 31"; break; }}
int main()
{ for( int i = 10; i < 32; ++i )
{ std::cout << i << ": "; bereiche( i ); std::cout << '\n'; }}std::cout
10: Bereich bis 19
11: Bereich bis 19
12: Bereich bis 19
13: Bereich bis 19
14: Bereich bis 19
15: Bereich bis 19
16: Bereich bis 19
17: Bereich bis 19
18: Bereich bis 19
19: Bereich bis 19
20: Bereich von 20 bis 29
21: Bereich von 20 bis 29
22: Bereich von 20 bis 29
23: Bereich von 20 bis 29
24: Bereich von 20 bis 29
25: Bereich von 20 bis 29
26: Bereich von 20 bis 29
27: Bereich von 20 bis 29
28: Bereich von 20 bis 29
29: Bereich von 20 bis 29
30: Zahl 30
31: Zahl 31
Variablendefinitionen
In einer 〈condition 〉 kann auch eine Variable definiert und initialisiert werden. Deren Gültigkeitsbereich ist dann die Anweisung 〈statement 〉 der switch -Anweisung. Der Wert der 〈condition 〉 ist der Wert der deklarierten Variablen (nötigenfalls nach impliziter Wandlung in einen ganzzahligen Typ oder Aufzählungstyp).
definition.cpp
#include <iostream>
#include <ostream>
void bereiche( int const zahl )
{ int const z = 99;
switch( int const z = zahl < 30 ? zahl < 20 ? 10 : 20 : zahl )
{ case 10: std::cout << "z = " << z << ", Fall A, "; break;
case 20: std::cout << "z = " << z << ", Fall B, "; break;
case 30: std::cout << "z = " << z << ", Fall C, "; break;
case 31: std::cout << "z = " << z << ", Fall D, "; break; }
std::cout << "z = " << z << ".\n"; }
int main()
{ for( int i = 10; i < 32; ++i )
{ std::cout << i << ": "; bereiche( i ); }}std::cout
10: z = 10. Fall A. z = 99.
11: z = 10. Fall A. z = 99.
12: z = 10. Fall A. z = 99.
13: z = 10. Fall A. z = 99.
14: z = 10. Fall A. z = 99.
15: z = 10. Fall A. z = 99.
16: z = 10. Fall A. z = 99.
17: z = 10. Fall A. z = 99.
18: z = 10. Fall A. z = 99.
19: z = 10. Fall A. z = 99.
20: z = 20. Fall B. z = 99.
21: z = 20. Fall B. z = 99.
22: z = 20. Fall B. z = 99.
23: z = 20. Fall B. z = 99.
24: z = 20. Fall B. z = 99.
25: z = 20. Fall B. z = 99.
26: z = 20. Fall B. z = 99.
27: z = 20. Fall B. z = 99.
28: z = 20. Fall B. z = 99.
29: z = 20. Fall B. z = 99.
30: z = 30. Fall C. z = 99.
31: z = 31. Fall D. z = 99.
Strukturierte Programmierung
Die Anweisung "break;" ermöglicht es, unstrukturierte Programme zu schreiben, weil es sich ja um eine Sprunganweisung handelt. In der switch -Anweisung kommt es darauf aber gar nicht mehr an, denn diese verwendet ja selber schon Sprünge zu Marken und ist insofern selber unstrukturiert. In der folgenden Funktion hat die markierte Anweisung "case false: std::cout << "Peter\n";" beispielsweise zwei „Eingänge“, da sie sowohl nach der vorangehenden Anweisung ausgeführt wird als auch, wenn sie angesprungen wird, weil der Ausdruck "gruss" den Wert "false" hat.
peter [Funktion]
void peter( bool const gruss )
{ switch( gruss )
{ case true: std::cout << "Hallo, ";
case false: std::cout << "Peter\n"; }}
Allerdings kann man die Switch -Anweisung zusammen mit der Break -Anweisung verwenden, um strukturiert zu programmieren, wenn man sicherstellt, daß die der Switch -Anweisung in den geschweiften Klammern nur Anweisungsfolgen ist, die mit Case -Marken beginnen und jeweils mit einem "break;" enden. Das folgende Beispiel zeigt also die strukturierte Version der Funktion "peter" mit zwei solchen Anweisungsfolgen.
peter1 [Funktion]
void peter1( bool const gruss )
{ switch( gruss )
{ case true: std::cout << "Hallo, Peter\n"; break;
case false: std::cout << "Peter\n"; break; }}
Durch die Verwendung dieses Schemas ist es jetzt nämlich sichergestellt, daß jede Anweisungsfolge in der Switch -Anweisung genau einen Eingang und genau einen Ausgang hat. Der Eingang sind die Chase -Marken am Anfang der Anweisungsfolge. Der Ausgang ist die Anweisung "break;" am Ende jeder Anweisungsfolge. Sprachen, die strukturierte Programmierung stärker unterstützen (oder „erzwingen“ wollen), erlauben gar keine andere Form der Verzweigung. In C++ kann man auch unstrukturiert programmieren.
Es kann dann auch als übersichtlich empfunden werden, auf jede Folge von Case -Marke immer eine Verbundanweisung folgen zu lassen, die mit einer Break -Anweisung endet. Jede Verbundanweisung ist dann praktisch ein Block mit einem Eingang und einem Ausgang. Eine so geschriebene Switch -Anweisung ist eine gute Annährung an eine strukturierte Mehrfachverzweigung. Man könnte sich Richtlinien vorstellen, nach denen nur solche Switch -Anweisungen verwendet werden sollen.
peter2 [Funktion]
void peter2( bool const gruss )
{ switch( gruss )
{ case true: { std::cout << "Hallo, Peter\n"; break; }
case false: { std::cout << "Peter\n"; break; }}}
Frage Switch-Anweisung in C++ (2008-01-15)
Von sic(6)SaNdMaN
Zu https://www.purl.org/stefan_ram/pub/c++_switch_de
Hallo.
Wieso kommt bei dem ersten Beispiel der switch Anweisung als Ausgabe:
"Hallo, Peter
Peter
"
raus ?? Die zweite (und evtl. dritte) Seite dürfte nicht erscheinen ...
Ausserdem würde ein "return 0;" die main-Funktion korrekter machen ... nur der Vollständigkeit halber.
Aber das Ergebnis des Mini-Programs oben will mir nicht in den Kopf.
Ach ja, und mit den kanonischen URLs dieser Seite(n):
Wenn ich z.B.:
https://www.purl.org/stefan_ram/pub/c++_switch_de
direkt in die Adressleiste (firefox) kopiere, bekomme ich die Acces Denied Fehlermeldung ... ich musste mir das google Sucherergebnis als Book
mark speichern und dort dann auf den Link klicken. Das ist echt mal komisch.
Aber auch etwas Lob an dieser Stelle:
Es is gut, dass es noch Leute gibt die gesammeltes Wissen zu Verfügung stellen.
Vielen Dank an dieser Stelle, da mir die Beispiele auf der Seite doch weitergeholfen haben.
MfG,
sic(6)SaNdMaN
Antwort 2008-01-15 Hallo sic(6)SaNdMaN,
ich danke für Ihre Anfrage!
- Die Ausgabe der Switch-Anweisung ergibt sich aus den Regeln der Programmiersprache C++ und wird in den beiden Absätzen über dem Quelltext erklärt. Sie könnten das Programm übersetzen und starten, um sich von der Richtigkeit der angegebenen Ausgabe zu überzeugen. Zum Verständnis hilft es dann vielleicht, die Erklärung und das Programm noch einmal zu lesen sowie verschiedene Variationen des Programms zu erfinden und auszuprobieren (entdeckendes Lernen).
- Die Regeln der Programmiersprache C++ erlauben es ausdrücklich, »return 0;« am Ende der Funktion »main« wegzulassen (ISO/IEC 14882:2003(E) 3.6.1p4).
- Die Fehlermeldung »Access Denied« könnte damit zu tun haben, daß ich etwas falsch konfiguriert habe. Ich werde mich bemühen, dies zu korrigieren.
Es freut mich, daß die Beispiele auf der Seite Ihnen weiterhelfen konnten!
Mit freundlichen Grüßen
Stefan Ram