Ein Wert, viele Konstanten
Eine else if-Kette, die immer wieder dieselbe Variable gegen Konstanten vergleicht, ist ein Muster mit einer eigenen Anweisung in C:
if (choice == 1) { /* ... */ }
else if (choice == 2) { /* ... */ }
else if (choice == 3) { /* ... */ }
else { /* ... */ }
switch sagt das direkter. Es wertet den Ausdruck einmal aus und springt dann direkt zum passenden Label.
Ändere choice und führe es erneut aus. Die Struktur macht die Menge der erlaubten Werte auf einen Blick sichtbar, und das ist der eigentliche Grund, sie hier zu bevorzugen.
Syntax und die Regeln für case-Label
switch (integerExpression) {
case CONSTANT_1:
Anweisungen
break;
case CONSTANT_2:
Anweisungen
break;
default:
Anweisungen
break;
}
Vier Einschränkungen, die C durchsetzt:
- Der switch-Ausdruck muss einen Ganzzahltyp haben -
int,char,short,longoder einenum. Nichtfloat, nichtdoubleund keine Zeichenkette. - Jedes
case-Label muss ein konstanter Ausdruck sein, der zur Übersetzungszeit bekannt ist.case n:mit einer Variablennist ein Fehler;case 3 + 4:undcase MAX:(ein#defineoder eine enum-Konstante) sind in Ordnung. - Zwei Fälle dürfen nicht denselben Wert haben. Duplikate sind ein Übersetzungsfehler, ein kleines, aber echtes Sicherheitsnetz.
- Ein Fall kann keinen Bereich ausdrücken.
case 1 ... 5:ist eine GCC-Erweiterung, kein Standard-C; nimm für Bereiche einif.
default ist optional und darf überall im Block stehen, obwohl es nahezu universelle Konvention ist, es ans Ende zu setzen. Ohne ein default überspringt ein nicht passender Wert einfach das gesamte switch.
break: warum es nicht optional ist
Das Überraschendste an switch in C ist, dass case-Label Sprungziele sind, keine Schachteln. Sobald die Ausführung auf einem Label landet, läuft sie durch jede Anweisung darunter weiter - über andere case-Label hinweg - bis zu einem break oder der schließenden Klammer.
Das erste switch gibt eins, zwei und drei aus - es traf case 1 und lief dann durch alles darunter. Das zweite gibt nur eins aus. Dieses Verhalten heißt Durchfallen, und ein vergessenes break ist der klassische switch-Bug in C: Das Programm ist still falsch, statt die Übersetzung zu verweigern.
Kompiliere mit gcc -Wall -Wextra -Wimplicit-fallthrough, und der Compiler zeigt auf jeden Fall, der in den nächsten fällt.
Das break beim letzten Fall ist technisch überflüssig - der Block endet dort ohnehin. Schreibe es trotzdem, damit ein später darunter hinzugefügter Fall nicht still ein Durchfallen erzeugt.
Absichtliches Durchfallen
Durchfallen ist eine Stärke, wenn es beabsichtigt ist. Label ohne Anweisungen dazwischen zu stapeln ist die gewöhnliche Art zu sagen „diese Werte tun dasselbe":
Drei Label teilen sich einen Rumpf, ohne doppelten Code und ohne lange ||-Bedingung. Compiler warnen bei dieser Form nicht, denn es gibt keine Anweisungen zum Durchfallen.
Durchfallen mit Anweisungen dazwischen - wo Fall 1 läuft und dann absichtlich in Fall 2 weiterläuft - ist seltener und verdient einen ausdrücklichen Kommentar, denn einem Leser muss gesagt werden, dass das fehlende break eine Entscheidung war:
switch (level) {
case 3:
printf("ausfuehrlich\n");
/* fall through */
case 2:
printf("info\n");
/* fall through */
case 1:
printf("fehler\n");
break;
}
/* fall through */ ist der Kommentar, den GCCs Warnung erkennt, er bringt die Meldung also auch zum Schweigen.
Über Zeichen schalten
Weil char ein Ganzzahltyp ist, passt die Zeichenklassifizierung natürlich:
Beachte, dass case 'a': einfache Anführungszeichen benutzt - eine Zeichenkonstante, die schlicht der Ganzzahlwert dieses Zeichens ist. case "a": wäre eine Zeichenkette und kompiliert nicht.
Über Enums schalten
Die beste Paarung in C ist switch mit einem enum. Die case-Label werden zu lesbaren Namen, und Compiler können warnen, wenn das switch nicht jeden Aufzählungswert behandelt:
Zwei nachahmenswerte Dinge hier. Jeder Fall macht ein return, es braucht also kein break - ein return verlässt die Funktion ganz, was auch das switch beendet. Und es gibt kein default: Mit -Wswitch (in -Wall enthalten) sorgt das Weglassen von default dafür, dass der Compiler an dem Tag warnt, an dem jemand einen fünften Zustand hinzufügt und diese Funktion vergisst. Ein default hätte das verborgen.
Variablen innerhalb eines switch deklarieren
Ein switch-Rumpf ist ein Block, eine Deklaration in einem Fall ist also in den folgenden sichtbar - wenn auch nicht unbedingt initialisiert. Über eine Initialisierung zu springen ist in C ein Fehler:
switch (n) {
case 1:
int x = 10; // Fehler in C: ein Label darf so keiner Deklaration vorangehen
printf("%d\n", x);
break;
case 2:
printf("%d\n", x); // x existiert hier, wurde aber nie initialisiert
break;
}
Wickle den Fall in eigene geschweifte Klammern, wenn er lokale Variablen braucht:
switch (n) {
case 1: {
int x = 10;
printf("%d\n", x);
break;
}
case 2:
printf("zwei\n");
break;
}
switch oder if-else?
Greife zu switch, wenn all das zutrifft: Du prüfst einen Ausdruck, er hat einen Ganzzahltyp, und du vergleichst ihn gegen feste Konstanten. Menüführung, enum-Zustandsmaschinen, Zeichenklassifizierung und Befehlsverteilung sind die kanonischen Fälle. Der Gewinn sind Lesbarkeit, die Prüfung auf doppelte Werte und ein Compiler, der vor unbehandelten Aufzählungswerten warnen kann.
Greife zu if-else, wenn die Prüfungen Bereiche (score >= 90), Gleitkommawerte, Zeichenketten (du brauchst strcmp), mehrere verschiedene Variablen oder mit && und || verknüpfte Bedingungen betreffen. Nichts davon lässt sich als case-Label ausdrücken.
Die Geschwindigkeit ist selten der entscheidende Faktor. Compiler machen aus einem dichten switch eine Sprungtabelle und aus einem dünnen Vergleiche, bei einer Handvoll Fälle sind die beiden Formen also nicht zu unterscheiden. Wähle die, die die Absicht ausspricht.
Häufig gestellte Fragen
Wie funktioniert eine switch-Anweisung in C?
switch (ausdruck) wertet einen Ganzzahlausdruck einmal aus, springt zu dem case-Label, dessen Konstante passt, und läuft von dort, bis es auf ein break oder das Ende des switch trifft. Passt nichts, springt es zu default, sofern eines vorhanden ist, und überspringt sonst den ganzen Block.
Warum brauche ich break in einem C-switch?
Weil case-Label Sprungziele sind, keine getrennten Blöcke. Ohne ein break läuft die Ausführung direkt in die Anweisungen des nächsten Falls weiter - das heißt Durchfallen. Ein fehlendes break ist der häufigste switch-Bug in C, und gcc -Wimplicit-fallthrough beanstandet ihn.
Kann man in C über eine Zeichenkette schalten?
Nein. Ein C-switch verlangt einen Ganzzahlausdruck, int, char und enum-Werte funktionieren also, Zeichenketten, Gleitkommazahlen und Bereiche nicht. Um anhand einer Zeichenkette zu verzweigen, nimm strcmp in einer if-else-Kette, oder bilde die Zeichenkette zuerst auf ein enum ab und schalte darüber.
Wann sollte ich in C switch statt if-else benutzen?
Nimm switch, wenn du eine Ganzzahl oder ein Zeichen gegen eine Liste fester Konstanten vergleichst - Menübefehle, enum-Zustände, Zeichenklassifizierung. Nimm eine if-else-Kette, wenn die Prüfungen Bereiche, mehrere Variablen oder irgendetwas anderes als Gleichheit mit einer Konstanten zur Übersetzungszeit betreffen.