Zuerst einige Hintergrundinformationen dazu, wie switch
(wirklich) funktioniert:
A switch
wird normalerweise als ein Konstrukt betrachtet, das einen Codeabschnitt auswählt, der abhängig vom Wert eines Ausdrucks ausgeführt wird, wie in
Es ist jedoch genauer, wenn Sie sich eine switch
als eine Form der berechneten goto
-Anweisung vorstellen. Zum Beispiel ist das Folgende vollkommen legal:
Abhängig vom Wert von x
springt das Programm entweder zu case 1
oder case 2
(oder nach switch
, wenn x
weder 1 noch 2 ist).
Ihr Programm wird als C kompiliert (mit Junk-Werten für x
und y
innerhalb der switch
, da die Initialisierungen übersprungen werden), aber nicht als C ++. Der Grund dafür ist, dass C ++ keinen Sprung zu einer case
-Markierung erlaubt, um die Initialisierung einer Variablen zu kreuzen. Für einfache Typen wie int
ist das Überspringen von int x;
erlaubt (da keine Initialisierung involviert ist), aber nicht über int x = 1;
.
Die Hauptmotivation für diesen Unterschied besteht wahrscheinlich darin, dass der Sprung zu einer case
-Etikette, die eine Initialisierung in C ++ kreuzt, nicht sicher wäre, wenn Konstruktoren beteiligt sind. Wenn beispielsweise C ++ ein case
Label nach einer Definition My_class my_object
innerhalb eines Bereichs zulassen würde, würde das Springen zu diesem case
Label den Konstruktor von my_object
überspringen, aber seinen Destruktor immer noch ausführen, wenn der Scope beendet wird.
Die gleichen Einschränkungen gelten für goto
in C ++. Sie können es nicht verwenden, um in einen Block zu springen und eine Variableninitialisierung zu überspringen.
Als Randnotiz folgt switch
der gleichen allgemeinen Syntax wie if
und while
. Die Syntax von if
, wie im C11-Standard (ISO / IEC 9899: 2011, Abschnitt 6.8.4) angegeben, lautet
if (Ausdruck) Anweisung
, während die Syntax von switch
switch (Ausdruck) Anweisung
Der einzige Unterschied in Bezug auf die Anweisung (in C - C ++ fügt einige weitere Einschränkungen wie oben erwähnt hinzu) besteht darin, dass case
labels (und break
) enthalten dürfen. für eine switch
, aber nicht für eine if
(es sei denn, die if
tritt innerhalb einer switch
auf).
Wie bei if
können Sie sogar die geschweiften Klammern abbrechen und Code wie folgt schreiben. (Ob das unnötig verwirrend ist, ist eine andere Diskussion.)
Syntaktisch gehören case
und default
Labels in die gleiche Kategorie wie goto
labels. Abschnitt 6.8.1 des C11-Standards hat die folgende Definition:
beschriftete Aussage:
Bezeichner : Anweisung
Fall Konstantenausdruck : Anweisung Standard : Anweisung
Sie können herausfinden, was mit einem einfachen Test passiert:
%Vor% Dieser Code wird innerhalb einer Funktion mit gcc
kompiliert. Der C-Compiler sieht i
und y
innerhalb eines Blocks deklariert, der durch die geschweiften Klammern begrenzt ist. Die printf
-Anweisungen drucken jedoch Junk für i
und y
, da die Zuweisungen niemals ausgeführt werden. Dies liegt daran, dass eine switch
-Anweisung einen Sprung zum case
bildet, der dem Ausdruck am Anfang von switch
entspricht. Der ausführbare Code zwischen der öffnenden Klammer und dem ersten case
kann also nicht erreicht werden. Siehe Warum können Variablen in einer switch-Anweisung nicht deklariert werden? , die nicht genau das gleiche Szenario erklärt, aber eine verwandte Diskussion über die switch
-Anweisung hat.
Wenn Sie dies mit aktivierten Warnungen ( gcc -Wall
) kompilieren, erhalten Sie:
Interessanterweise wird der folgende Code ohne Warnungen kompiliert und funktioniert:
%Vor% Die Variablen werden so gedruckt, wie Sie es erwarten, da sie im Rahmen des Blocks switch
deklariert sind, und die Werte in den Abschnitten case
, in denen die Ausführung stattfindet. Die Tatsache, dass es in diesem Fall funktioniert, bedeutet nicht, dass es empfohlene Praxis ist. :)
Kontrolle kann Anweisungen zwischen dem switch
head und dem ersten case
Ausdruck erreichen - aber einige andere Kontrollstrukturen müssen es dorthin senden. Zum Beispiel gibt es nichts, was Sie daran hindert, einen Switch und eine Schleife zu verschachteln :
kompiliert ohne irgendwelche Beschwerden von entweder gcc 4.9 oder clang 3.5 im -std=c11
-Modus, wenn die Warnungen so hoch wie sie gehen, und wenn sie ausgeführt wird, druckt:
Fun fact: Wenn die Optimierung aktiviert ist, erzeugen beide Compiler tatsächlich 100% geradlinigen Code:
%Vor%Es ist nicht legal, und wenn Sie kompilieren, erhalten Sie eine Reihe von "Kreuze Initialisierung von ..." Fehlern. Es gibt eine wirklich gute Erklärung dafür, was hier vor sich geht: Fehler bei der Initialisierung einer Reihe von Kreuzen
Im Prinzip funktioniert ein Schalterfall, indem man zwischen den case-Anweisungen einen beliebigen Code springt, und es ist nicht erlaubt, über die Initialisierung einer Variablen hinaus zu springen. Es wird technisch legal sein und kompilieren, wenn Sie nur einen Funktionsaufruf zwischen den beiden setzen, obwohl die Funktion nie tatsächlich aufgerufen wird.
%Vor%Ich habe versucht, einen Code ähnlich dem, was Sie fragen, zu kompilieren, es kommt mindestens ein Fehler heraus:
Fehler C2360: Initialisierung von 'x' wird von 'case' Label
übersprungen
Ihr extra Code wurde nie ausgeführt, weil nach dem Umschalten die Anweisung 'test' überprüft wird, dann wird es zum entsprechenden Label gehen, Ihr extra Bit Code ist unter keinem Label, deshalb wird es für immer übersprungen.
Warum können Sie vor der switch-Anweisung nicht Ihren zusätzlichen Code einfügen? irgendeinen Grund?