Ich habe mich immer gefragt: Warum können Sie Variablen nach einer Fallbezeichnung in einer switch-Anweisung nicht deklarieren? In C ++ können Sie Variablen praktisch überall deklarieren (und es ist natürlich eine gute Sache, sie kurz vor der ersten Verwendung zu deklarieren), aber Folgendes funktioniert immer noch nicht:
switch (val)
{
case VAL:
// This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
Das Obige gibt mir den folgenden Fehler (MSC):
Die Initialisierung von 'newVal' wird vom 'case'-Label übersprungen
Dies scheint auch in anderen Sprachen eine Einschränkung zu sein. Warum ist das so ein Problem?
c++
switch-statement
rauben
quelle
quelle
Antworten:
Case
Anweisungen sind nur Bezeichnungen . Dies bedeutet, dass der Compiler dies als einen Sprung direkt zum Label interpretiert. In C ++ liegt das Problem hier im Bereich. Ihre geschweiften Klammern definieren den Bereich als alles in derswitch
Anweisung. Dies bedeutet, dass Sie einen Bereich haben, in dem ein Sprung weiter in den Code ausgeführt wird, wobei die Initialisierung übersprungen wird.Der richtige Weg, dies zu handhaben, besteht darin, einen für diese
case
Anweisung spezifischen Bereich zu definieren und Ihre Variable darin zu definieren:quelle
Diese Frage
istursprünglich als [C] und [C ++] zur gleichen Zeit markiert. Der ursprüngliche Code ist zwar sowohl in C als auch in C ++ ungültig, jedoch aus völlig anderen, nicht verwandten Gründen.In C ++ ist dieser Code ungültig, da das
case ANOTHER_VAL:
Label unternewVal
Umgehung seiner Initialisierung in den Bereich der Variablen springt . Sprünge, die die Initialisierung automatischer Objekte umgehen, sind in C ++ unzulässig. Diese Seite des Problems wird von den meisten Antworten korrekt angesprochen.In der C-Sprache ist das Umgehen der Variableninitialisierung jedoch kein Fehler. Das Springen in den Bereich einer Variablen über ihre Initialisierung ist in C legal. Dies bedeutet einfach, dass die Variable nicht initialisiert bleibt. Der ursprüngliche Code wird aus einem ganz anderen Grund nicht in C kompiliert. Die Beschriftung
case VAL:
im Originalcode ist an die Deklaration der Variablen angehängtnewVal
. In der Sprache C sind Deklarationen keine Aussagen. Sie können nicht beschriftet werden. Und genau das verursacht den Fehler, wenn dieser Code als C-Code interpretiert wird.Das Hinzufügen eines zusätzlichen
{}
Blocks behebt sowohl C ++ - als auch C-Probleme, obwohl diese Probleme sehr unterschiedlich sind. Auf der C ++ - Seite wird der Umfang von eingeschränktnewVal
, und es wird sichergestellt, dasscase ANOTHER_VAL:
nicht mehr in diesen Bereich gesprungen wird, wodurch das C ++ - Problem beseitigt wird. Auf der C-Seite führt dieses Extra{}
eine zusammengesetzte Anweisung ein, wodurch dascase VAL:
Label auf eine Anweisung angewendet wird, wodurch das C-Problem beseitigt wird.In C Fall kann das Problem leicht ohne die gelöst werden
{}
. Fügen Sie einfach eine leere Anweisung nach demcase VAL:
Etikett hinzu und der Code wird gültigBeachten Sie, dass es aus C ++ -Sicht ungültig bleibt, obwohl es jetzt aus C-Sicht gültig ist.
Symmetrisch kann in C ++ das Problem ohne das leicht gelöst werden
{}
. Entfernen Sie einfach den Initialisierer aus der Variablendeklaration und der Code wird gültigBeachten Sie, dass es, obwohl es jetzt aus C ++ - Sicht gültig ist, aus C-Sicht ungültig bleibt.
quelle
newVal
wann es springtANOTHER_VAL
?case ANOTHER_VAL:
Punkt ist die VariablenewVal
sichtbar, jedoch mit unbestimmtem Wert.§A9.3: Compound Statement
von K & R C (zweite Ausgabe). Der Eintrag erwähnte die technische Definition einer zusammengesetzten Aussage, die ist{declaration-list[opt] statement-list[opt]}
. Verwirrt, weil ich gedacht hatte, eine Erklärung sei eine Aussage, habe ich sie nachgeschlagen und sofort diese Frage gefunden, ein Beispiel, bei dem diese Ungleichheit offensichtlich wird und tatsächlich ein Programm bricht . Ich glaube, eine andere Lösung (für C) wäre, eine andere Aussage (möglicherweise eine Null-Aussage?) Vor die Deklaration zu setzen, damit die beschriftete Aussage erfüllt ist.OK. Nur um dies klar zu stellen, hat dies nichts mit der Erklärung zu tun. Es bezieht sich nur auf "Überspringen der Initialisierung" (ISO C ++ '03 6.7 / 3)
In vielen Beiträgen wurde erwähnt, dass das Überspringen der Deklaration dazu führen kann, dass die Variable "nicht deklariert" wird. Das ist nicht wahr. Ein POD-Objekt kann ohne Initialisierer deklariert werden, hat jedoch einen unbestimmten Wert. Zum Beispiel:
Wenn das Objekt kein POD oder Aggregat ist, fügt der Compiler implizit einen Initialisierer hinzu, sodass es nicht möglich ist, über eine solche Deklaration zu springen:
Diese Einschränkung ist nicht auf die switch-Anweisung beschränkt. Es ist auch ein Fehler, 'goto' zu verwenden, um über eine Initialisierung zu springen:
Ein bisschen Trivia ist, dass dies ein Unterschied zwischen C ++ und C ist. In C ist es kein Fehler, über die Initialisierung zu springen.
Wie bereits erwähnt, besteht die Lösung darin, einen verschachtelten Block hinzuzufügen, sodass die Lebensdauer der Variablen auf die Einzelfallbezeichnung beschränkt ist.
quelle
Die gesamte switch-Anweisung befindet sich im selben Bereich. Um dies zu umgehen, gehen Sie folgendermaßen vor:
Beachten Sie die Klammern.
quelle
Nachdem ich alle Antworten und weitere Nachforschungen gelesen habe, bekomme ich ein paar Dinge.
In C gemäß der Spezifikation,
§6.8.1 Beschriftete Aussagen:
In C gibt es keine Klausel, die eine "gekennzeichnete Deklaration" zulässt. Es ist einfach nicht Teil der Sprache.
Damit
Dies wird nicht kompiliert , siehe http://codepad.org/YiyLQTYw . GCC gibt einen Fehler aus:
Sogar
Dies wird auch nicht kompiliert , siehe http://codepad.org/BXnRD3bu . Hier bekomme ich auch den gleichen Fehler.
In C ++ gemäß der Spezifikation,
Die Labeled-Deklaration ist zulässig, die Labeled-Initialisierung jedoch nicht.
Siehe http://codepad.org/ZmQ0IyDG .
Die Lösung für einen solchen Zustand ist zwei
Verwenden Sie entweder einen neuen Bereich mit {}
Oder verwenden Sie eine Dummy-Anweisung mit Label
Deklarieren Sie die Variable vor switch () und initialisieren Sie sie mit unterschiedlichen Werten in der case-Anweisung, wenn sie Ihre Anforderung erfüllt
Noch ein paar Dinge mit switch-Anweisung
Schreiben Sie niemals Anweisungen in den Schalter, die nicht Teil eines Labels sind, da diese niemals ausgeführt werden:
Siehe http://codepad.org/PA1quYX3 .
quelle
a
in den Bereich der Variablena
. Aus C-Sicht liegt das Problem also beimcase VAL:
Etikett, und Sie haben es richtig beschrieben. Aus C ++ - Sicht liegt das Problem jedoch bei dercase ANOTHER_VAL:
Beschriftung.Sie können dies nicht tun, da
case
Beschriftungen eigentlich nur Einstiegspunkte in den enthaltenen Block sind.Dies wird am deutlichsten durch Duffs Gerät veranschaulicht . Hier ist ein Code aus Wikipedia:
Beachten Sie, dass die
case
Beschriftungen die Blockgrenzen vollständig ignorieren. Ja, das ist böse. Aus diesem Grund funktioniert Ihr Codebeispiel nicht. Das Springen zu einemcase
Label entspricht dem Verwenden vongoto
, sodass Sie mit einem Konstruktor nicht über eine lokale Variable springen dürfen.Wie mehrere andere Poster angegeben haben, müssen Sie einen eigenen Block einfügen:
quelle
SAR
in x86 im VergleichSHR
zu vorzeichenlosen Schichten).Die meisten der bisherigen Antworten sind in einer Hinsicht falsch: Sie können Variablen nach der case-Anweisung deklarieren, aber nicht initialisieren:
Wie bereits erwähnt, besteht eine gute Möglichkeit darin, Klammern zu verwenden, um einen Bereich für Ihren Fall zu erstellen.
quelle
Mein bevorzugter böser Trick ist es, mit einem if (0) ein unerwünschtes Falletikett zu überspringen.
Aber sehr böse.
quelle
goto
ohne Verschleierung des Codes zum gewünschten Etikett springenVersuche dies:
quelle
Sie können Variablen innerhalb einer switch-Anweisung deklarieren, wenn Sie einen neuen Block starten:
Der Grund liegt darin, Speicherplatz auf dem Stapel für die Speicherung der lokalen Variablen zuzuweisen (und zurückzugewinnen).
quelle
Erwägen:
Wenn keine break-Anweisungen vorhanden sind, wird newVal manchmal zweimal deklariert, und Sie wissen erst zur Laufzeit, ob dies der Fall ist. Ich vermute, dass die Einschränkung auf diese Art von Verwirrung zurückzuführen ist. Was wäre der Umfang von newVal? Die Konvention würde vorschreiben, dass es sich um den gesamten Schaltblock (zwischen den Klammern) handelt.
Ich bin kein C ++ - Programmierer, aber in C:
Funktioniert gut. Das Deklarieren einer Variablen innerhalb eines Schalterblocks ist in Ordnung. Nach einem Fallwächter zu deklarieren ist nicht.
quelle
Der gesamte Abschnitt des Switches ist ein einzelner Deklarationskontext. In einer solchen case-Anweisung können Sie keine Variable deklarieren. Versuchen Sie stattdessen Folgendes:
quelle
Wenn Ihr Code "int newVal = 42" sagt, würden Sie vernünftigerweise erwarten, dass newVal niemals nicht initialisiert wird. Aber wenn Sie über diese Aussage hinweg sind (was Sie tun), dann passiert genau das - newVal ist im Geltungsbereich, wurde aber nicht zugewiesen.
Wenn es das ist, was Sie wirklich wollten, muss die Sprache es explizit machen, indem Sie "int newVal; newVal = 42;" sagen. Andernfalls können Sie den Umfang von newVal auf den Einzelfall beschränken, was wahrscheinlicher ist, als Sie wollten.
Es kann Dinge verdeutlichen, wenn Sie dasselbe Beispiel betrachten, aber mit "const int newVal = 42;"
quelle
Ich wollte nur betonen , schlank ‚s Punkt . Ein Switch-Konstrukt schafft einen ganzen, erstklassigen Bürgerbereich. Es ist also möglich, eine Variable in einer switch-Anweisung vor der ersten Fallbezeichnung ohne ein zusätzliches Klammerpaar zu deklarieren (und zu initialisieren) :
quelle
int newVal
wird ausgeführt, nicht jedoch die= 42
Zuordnung.Bisher waren die Antworten für C ++.
Für C ++ können Sie nicht über eine Initialisierung springen. Sie können in C. In C ist eine Deklaration jedoch keine Anweisung, und auf Fallbezeichnungen müssen Anweisungen folgen.
Also, gültiges (aber hässliches) C, ungültiges C ++
Umgekehrt ist in C ++ eine Deklaration eine Anweisung, daher ist das folgende C ++ gültig, ungültiges C.
quelle
Interessant, dass das in Ordnung ist:
... aber das ist nicht:
Ich verstehe, dass ein Fix einfach genug ist, aber ich verstehe noch nicht, warum das erste Beispiel den Compiler nicht stört. Wie bereits erwähnt (2 Jahre zuvor, hehe), verursacht die Deklaration trotz der Logik nicht den Fehler. Initialisierung ist das Problem. Wenn die Variable in den verschiedenen Zeilen initialisiert und deklariert wird, wird sie kompiliert.
quelle
Ich habe diese Antwort ursprünglich für diese Frage geschrieben . Als ich damit fertig war, stellte ich fest, dass die Antwort geschlossen wurde. Also habe ich es hier gepostet, vielleicht findet es jemand hilfreich, der Verweise auf Standard mag.
Ursprünglicher Code in Frage:
Es gibt tatsächlich 2 Fragen:
1. Warum kann ich eine Variable nach dem
case
Label deklarieren ?Es ist, weil in C ++ Label in der Form sein muss:
N3337 6.1 / 1
Und in der
C++
Erklärung gilt die Aussage auch als Aussage (im Gegensatz zuC
):N3337 6/1:
2. Warum kann ich über die Variablendeklaration springen und sie dann verwenden?
Weil: N3337 6.7 / 3
Da
k
es sich um einen skalaren Typ handelt und zum Zeitpunkt der Deklaration nicht initialisiert wird, ist ein Überspringen der Deklaration möglich. Dies ist semantisch äquivalent:Dies wäre jedoch nicht möglich, wenn
x
es zum Zeitpunkt der Deklaration initialisiert würde :quelle
Neue Variablen können nur im Blockbereich dekaliert werden. Sie müssen so etwas schreiben:
Natürlich hat newVal nur innerhalb der Klammern Spielraum ...
Prost, Ralph
quelle
Ein
switch
Block ist nicht dasselbe wie eine Folge vonif/else if
Blöcken. Ich bin überrascht, dass keine andere Antwort dies klar erklärt.Betrachten Sie diese
switch
Aussage:Es mag überraschen, aber der Compiler wird es nicht als einfach ansehen
if/else if
. Es wird der folgende Code erzeugt:Die
case
Anweisungen werden in Labels konvertiert und dann mit aufgerufengoto
. Die Klammern erstellen einen neuen Bereich und es ist jetzt leicht zu erkennen, warum Sie nicht zwei Variablen mit demselben Namen innerhalb von a deklarieren könnenswitch
Blocks .Es mag seltsam aussehen, aber es ist notwendig, Fallthrough zu unterstützen ( dh nicht zu verwenden
break
, um die Ausführung zum nächsten fortzusetzencase
).quelle
Ich glaube, das Problem ist, dass die Anweisung übersprungen wurde und Sie versucht haben, die Variable an anderer Stelle zu verwenden. Sie würde nicht deklariert.
quelle
newVal existiert im gesamten Bereich des Schalters, wird jedoch nur initialisiert, wenn das VAL-Glied getroffen wird. Wenn Sie einen Block um den Code in VAL erstellen, sollte dieser in Ordnung sein.
quelle
C ++ Standard hat: Es ist möglich, in einen Block zu übertragen, jedoch nicht so, dass Deklarationen bei der Initialisierung umgangen werden. Ein Programm, das von einem Punkt, an dem sich eine lokale Variable mit automatischer Speicherdauer nicht im Gültigkeitsbereich befindet, zu einem Punkt springt, an dem sie sich im Gültigkeitsbereich befindet, ist fehlerhaft, es sei denn, die Variable hat den POD-Typ (3.9) und wird ohne Initialisierer (8.5) deklariert.
Der Code zur Veranschaulichung dieser Regel:
Der Code zum Anzeigen des Initialisierungseffekts:
quelle
Es scheint, dass anonyme Objekte in einer switch case-Anweisung deklariert oder erstellt werden können , weil sie nicht referenziert werden können und als solche nicht zum nächsten Fall durchfallen können. Betrachten Sie dieses Beispiel, das unter GCC 4.5.3 und Visual Studio 2008 kompiliert wurde (möglicherweise ein Compliance-Problem, obwohl Experten bitte abwägen).
quelle
const
Referenz mit einem eigenen Geltungsbereich gebunden ). Es ist ein Ausdruck, der in seiner Aussage lebt und stirbt (wo immer das auch sein mag). Daher ist es völlig irrelevant.Foo();
ist keine Erklärung; Die Frage betrifft Erklärungen.