Warum können keine Variablen deklariert werden, die in einer switch-Anweisung?
Ich habe mich immer gefragt, diese - warum kann man keine Variablen deklarieren, nach einer case-label in switch-Anweisung? In C++ kann man Variablen deklarieren, so ziemlich überall (und deklarieren Sie in der Nähe der ersten Nutzung ist natürlich eine gute Sache), aber Folgendes noch nicht:
switch (val)
{
case VAL:
//This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
Den oben genannten gibt mir die folgende Fehlermeldung (MSC):
Initialisierung von 'newVal' übersprungen 'Fall' label
Scheint dies eine Einschränkung auch in anderen Sprachen. Warum ist das so ein problem?
Für eine Erklärung, basierend auf der C-BNF-Grammatik, siehe stackoverflow.com/questions/1180550/weird-switch-error-in-obj-c/...
Hier ist ein wirklich gut Lesen über switch-Anweisungen und Etiketten (ABC:) im Allgemeinen.
Ich würde sagen, 'Warum kann nicht Variablen initialisiert werden, die in einer switch-Anweisung anstelle deklariert'.Da nur die Deklaration der Variablen geben Sie mir nur eine Warnung in MSVC.
Hier ist ein wirklich gut Lesen über switch-Anweisungen und Etiketten (ABC:) im Allgemeinen.
Ich würde sagen, 'Warum kann nicht Variablen initialisiert werden, die in einer switch-Anweisung anstelle deklariert'.Da nur die Deklaration der Variablen geben Sie mir nur eine Warnung in MSVC.
InformationsquelleAutor Rob | 2008-09-18
Du musst angemeldet sein, um einen Kommentar abzugeben.
Case
Aussagen sind nur Etiketten. Dies bedeutet, dass der compiler interpretiert dies als ein Klick direkt auf das Etikett. In C++, das problem hier ist einer der Rahmen. Ihre geschweiften Klammern definieren den Bereich als alles in derswitch
- Anweisung. Dies bedeutet, dass Sie mit Links sind ein Bereich, wo ein Sprung ausgeführt werden, weiter in den code überspringen der Initialisierung. Der richtige Umgang mit diesen ist für die Definition eines Bereichs spezifischecase
- Anweisung und definieren Sie Ihre Variablen innerhalb von.Ich Stimme mit Jeff - es ist alles zu einfach, zu "übernehmen" - Bereich, wenn das Lesen einer switch-Anweisung, weil das Einrücken von Stil, dass die meisten Menschen verwenden. Mein eigener Stil ist immer offen, einen neuen Bereich für jeden case/default, wenn es mehr als eine Zeile lang.
workmad3 - Kannst du mich finden alle C++ - compiler bei allen, die erzeugt einen neuen stack-frame, wenn Sie sich nicht deklarieren der neuen Variablen? Sie besorgt mir kurz auf, aber keiner von G++ 3.1, Visual C++ 7 oder Intel-C++ - 8 generieren von code für neue Bereiche, in denen Sie nicht deklarieren Sie keine Variablen.
durch Eingabe eines neuen geschweiften Klammern ein block nicht dazu führen, dass ein neuer stack-frame stackoverflow.com/questions/2759371/...
Ich weiß nicht, was 'die alten Tage' Sie sich beziehen. Ich habe nie begegnet einem compiler, wo alle Platz auf dem stack für eine Methode nicht zugewiesen, wenn die Methode eingegeben wird, in 40 Jahren.
InformationsquelleAutor TJ Seabrooks
Diese Frage
istwar ursprünglich markiert als [C] und [C++] an der gleichen Zeit. Der ursprüngliche code ist ja invalid in C und C++, aber aus ganz anderen nicht miteinander verwandten Gründen. Ich glaube, dass dieses wichtige detail übersehen (oder verschleiert), die durch die bestehenden Antworten.In C++ ist das der code ungültig ist, weil die
case ANOTHER_VAL:
label springt in den Geltungsbereich von VariablennewVal
unter Umgehung seiner Initialisierung. Sprünge, die bypass-Initialisierung von lokalen Objekten sind illegal in C++. Diese Seite des Problems ist richtig angesprochen von den meisten Antworten.Jedoch in der Programmiersprache C unter Umgehung der Initialisierung der Variablen ist nicht ein Fehler. Springen in der Gültigkeitsbereich einer Variablen über seine Initialisierung ist legal in C. Es bedeutet einfach, dass die variable Links ist nicht initialisiert. Der ursprüngliche code nicht kompilieren, in C aus einem ganz anderen Grund. Label
case VAL:
im original-code ist im Anhang der Erklärung der variablenewVal
. In der Programmiersprache C-Deklarationen sind keine Aussagen. Sie können nicht beschriftet werden. Und dies ist, was bewirkt, dass der Fehler bei diesem code wird interpretiert als C-code.Hinzufügen eines zusätzlichen
{}
block-Fehlerbehebungen für C++ und C Probleme, obwohl diese Probleme auftreten, sehr unterschiedlich sein. Auf der C++ Seite schränkt den Umfang dernewVal
achten Sie darauf, dasscase ANOTHER_VAL:
springt nicht mehr in diesem Bereich, wodurch die C++ - Problem. Auf der C-Seite, die extra{}
stellt eine zusammengesetzte Anweisung, so dass diecase VAL:
label anwenden, um eine Aussage, die nicht mit C Problem.Im C-Fall das problem kann leicht gelöst werden, ohne die
{}
. Fügen Sie einfach eine leere Anweisung nach dercase VAL:
label und der code wird gültigBeachten Sie, dass auch wenn es nun gilt aus C-Sicht, es bleibt ungültig, aus C++ - Sicht.
Symmetrisch, im C++ Fall das problem kann leicht gelöst werden, ohne die
{}
. Entfernen Sie einfach die Initialisierung von Variablen-Deklaration und der code wird gültigBeachten Sie, dass auch wenn es nun gilt aus C++ - Sicht, es bleibt ungültig, aus C-Sicht.
newVal
wenn es springtANOTHER_VAL
?Ja, es immer noch springt. Allerdings, wenn ich sage "es behebt das Problem" meine ich , dass es Updates der C++ - compiler Fehler. In C++ ist es illegal, überspringen einer skalaren Erklärung Initialisierung, aber es ist völlig in Ordnung, so überspringen Sie eine Skalar-Deklaration ohne Initialisierung. Bei
case ANOTHER_VAL:
Punkt variablenewVal
sichtbar ist, aber mit unbestimmten Wert.Faszinierend. Ich fand diese Frage nach der Lektüre
§A9.3: Compound Statement
von K&R-C (zweite Ausgabe). Der Eintrag erwähnt die technische definition eines compound-statement, die{declaration-list[opt] statement-list[opt]}
. Verwirrt, weil ich gedacht hatte, die Erklärung WAR eine Aussage, die ich sah es auf und fand sofort diese Frage, ein Beispiel, wo dieser Diskrepanz wird deutlich, und eigentlich Pausen ein Programm. Ich glaube, eine andere Lösung (für C) wäre eine weitere Aussage, die (möglicherweise eine null-Aussage?) bevor die Erklärung so, dass die Bezeichnung-Anweisung ist zufrieden.Hoppla, mir ist gerade aufgefallen, dass die null-Anweisung die Lösung die ich vorgeschlagen, die bereits in Ihrer Antwort. Never mind, dann.
Die beste Lösung ist wahrscheinlich zu einfach deklarieren Sie die variable außerhalb der switch-Anweisung.
InformationsquelleAutor AnT
Ok. Nur um dies zu verdeutlichen streng nichts zu tun hat mit der Erklärung. Es bezieht sich nur auf "einen Sprung über die Initialisierung" (ISO C++ '03 6.7/3)
Viele der Beiträge hier haben erwähnt, dass springen über die Erklärung kann Ergebnis in der variable "nicht deklariert". Das ist nicht wahr. Ein POD-Objekt deklariert werden kann ohne Initialisierer, aber es wird ein unbestimmter Wert. Zum Beispiel:
Wo das Objekt ist ein nicht-POD oder ein Aggregat der compiler implizit Hinzugefügt einen Initialisierer, und so ist es nicht möglich, springen über solch eine Erklärung:
Diese Einschränkung ist nicht nur auf die switch-Anweisung. Es ist auch ein Fehler für die Verwendung von 'springen', springen über eine Initialisierung:
Ein bisschen trivia ist, dass dies einen Unterschied zwischen C++ und C. In C ist es kein Fehler, springe über die Initialisierung.
Wie andere erwähnt haben, die Lösung ist das hinzufügen einer verschachtelten block, so dass die Lebensdauer von Variablen beschränkt sich auf den einzelnen Fall label.
Es ist illegal in C++. ISO-C++ '03 - 6.7/3: "...Ein Programm, springt von einem Punkt, wo eine lokale variable mit automatischer Speicherdauer ist nicht im Umfang bis zu einem Punkt, wo es im Rahmen ist schlecht ausgebildet, es sei denn, die variable POD-Typ (3.9) und erklärt wird, ohne eine Initialisierung (8.5)."
Ja, aber es ist nicht illegal in C (mindestens gcc sagt es nicht). j wird nicht initialisiert (haben einige zufällige Nummer), aber der compiler kompiliert es. Jedoch, im Fall der switch-Anweisung, die der compiler nicht selbst kompilieren Sie es und ich kann nicht erkennen den Unterschied zwischen einem goto/label Fall und ein switch case.
Im Allgemeinen eine einzige compiler-Verhalten nicht unbedingt whtat eigentlich erlaubt durch die Sprache. Ich habe beide C'90 und C'99 und beide Normen enthalten ein Beispiel mit einem Sprung über die Initialisierung in einer switch-Anweisung.
InformationsquelleAutor Richard Corden
Die ganze switch-Anweisung ist in demselben Umfang. Zu umgehen, gehen Sie folgendermaßen vor:
Hinweis die Klammern.
InformationsquelleAutor Mark Ingram
Nach der Lektüre alle Antworten und einige mehr Forschung, ich ein paar Sachen besorgen.
In C, entsprechend der Spezifikation,
§6.8.1 Labeled Statements:
In C gibt es keine Klausel, die ermöglicht, dass eine "beschriftet Erklärung". Es ist einfach nicht Teil der Sprache.
So
Diese wird nicht kompiliert, siehe http://codepad.org/YiyLQTYw. GCC gibt eine Fehlermeldung:
Sogar
dies ist auch nicht kompilieren, siehe http://codepad.org/BXnRD3bu. Hier bin ich auch immer die gleichen Fehler.
In C++, die nach der Spezifikation,
beschriftet-Deklaration ist erlaubt, aber beschriftet -Initialisierung ist nicht erlaubt.
Sehen http://codepad.org/ZmQ0IyDG.
Lösung für diese Bedingung ist, zwei
Entweder neue Bereich mit {}
Oder verwenden Sie dummy-Anweisung mit label
Deklarieren Sie die variable vor dem Schalter (), und initialisieren Sie es mit verschiedenen Werten in case-Anweisung, wenn Sie erfüllt Ihre Anforderung
Einige weitere Dinge, die mit der switch-Anweisung
Nie schreiben, Aussagen auf den Schalter, die nicht Teil von jedem Etikett, da Sie nie ausgeführt wird:
Sehen http://codepad.org/PA1quYX3.
a
in den Geltungsbereich von Variablena
. So, aus C-Sicht, die Probleme mitcase VAL:
label und Sie beschreiben es richtig. Aber aus C++ - Sicht, das problem mitcase ANOTHER_VAL:
label.In C++ anders als in C, die Erklärungen sind eine Teilmenge der Aussagen.
InformationsquelleAutor Jeegar Patel
Können Sie dies nicht tun, weil
case
Etiketten sind eigentlich nur Einstiegspunkte in den enthaltenden block.Dies ist besonders deutlich Duff ' s device. Hier finden Sie den code aus der Wikipedia:
Beachten Sie, wie die
case
Etiketten Total ignorieren den block hinweg. Ja, das ist übel. Aber das ist, warum Ihr code-Beispiel funktioniert nicht. Springen zu einercase
Etikett ist das gleiche wie mitgoto
, so dass Sie nicht erlaubt, um zu springen über eine lokale variable mit einem Konstruktor.Wie mehrere andere Poster, die angegeben haben, müssen Sie einen block mit Ihren eigenen:
Was?! In einem zwei-Komplement-system, der Fehler hat keinen Einfluss auf modulos durch eine Potenz von 2 ist (es ist nur eine UND auf der Unterseite bits), und nicht auf Divisionen durch Potenzen von 2, so lange, wie Sie Ihre Prozessor-Architektur hat sich ein arithmetischer rechts-shift-operation (
SAR
im x86-VergleichSHR
für unsigned Schichten).Ich glaube er meint, wenn der compiler zulassen müssen, dass bei negativen Werten, wo "nur eine UND auf der Unterseite bits" nicht zu halten; zum Beispiel, -1 % 8 gibt -1 auf das zwei-Komplement-system mit g++ (auf dem Schild in diesem Fall ist die Umsetzung definiert pro 5.6/4).
Ich Stimme mit Ihnen überein, dass R ist zu übertreiben die Wirkung; ich sah nur Ihren Kommentar und wusste, dass eine einfache UND nicht ausreichen.
Erwähnenswert ist auch die original-Wikipedia-code ist für das senden von Daten in eine memory-mapped-Ausgabe, die sieht seltsam aus, weil hier nicht erwähnt und jedes byte kopiert, um den gleichen "zu" Position. Umgehen könnte, entweder durch hinzufügen der postfix - ++ " an, oder die Erwähnung der use-case ist für die memory-mapped IO. Ganz peripher auf die ursprüngliche Frage :-).
InformationsquelleAutor emk
Meisten Antworten bisher sind falsch in einer Hinsicht: Sie kann Variablen deklarieren, die nach der case-Anweisung, aber Sie nicht initialisieren:
Wie bereits erwähnt, ein schöner Weg, um dieses ist die Verwendung von Klammern, um einen Bereich erstellen für Ihren Fall.
Herr 32 hast du falsch verstanden, was dein Fehler ist: ja das geht nicht zu kompilieren, aber nicht, weil Sie deklarieren eine variable innerhalb einer switch. Der Fehler ist weil Sie versuchen, eine variable zu deklarieren, nachdem eine Anweisung, das illegal in C.
Jetzt ein Tage, das legal in c90 und der neueren version des c
InformationsquelleAutor MrZebra
Meine Lieblings-böse-Schalter trick ist, verwenden Sie eine if - (0) zum überspringen eines unerwünschten Fall label.
Aber sehr böse.
Sehr schön. Beispiel warum: Fall 0 und Fall 1 könnte zum Beispiel die Initialisierung einer variable anders an, die dann in Fall 2.
Wenn Sie wollen, sowohl Fall 0 und Fall 1, fall durch Fall 2. ( ohne case 0 Fall durch Fall 1 ). Weiß nicht, ob es wirklich nützlich ist, aber sicher funktioniert.
Sie können nur springen, um die erforderliche Etikett mit
goto
ohne Verschleierung des CodesInformationsquelleAutor Jeremy
Versuchen Sie dies:
InformationsquelleAutor Dan Shield
Können Sie Variablen deklarieren, innerhalb einer switch-Anweisung wenn Sie einen neuen block beginnen:
Grund hat zu tun mit der Zuteilung (und Rückforderung) Platz auf dem stack für die Speicherung der lokalen Variablen(s).
InformationsquelleAutor Seb Rose
Betrachten:
In der Abwesenheit von break-Anweisungen, manchmal newVal bekommt erklärt, zweimal, und Sie nicht wissen, ob es nicht erst zur Laufzeit. Meine Vermutung ist, dass die Beschränkung wegen dieser Art von Verwirrung. Was würde der Umfang der newVal werden? Übereinkommen diktieren würde, wäre es der gesamte switch-block (in geschweiften Klammern).
Ich bin kein C++ Programmierer, aber in C:
Funktioniert einwandfrei. Die Deklaration einer Variablen innerhalb eines switch-Blocks ist in Ordnung. Deklarieren nach einer case-guard nicht.
eigentlich das Beispiel zeigt, dass ein printf nicht ausgeführt, aber in diesem Fall, int x nicht eine Erklärung, sondern eine Erklärung an, in der x deklariert, wird Speicherplatz für Sie reserviert ist, jedes mal, wenn Sie die Funktion Umfeld wird gestapelt, siehe: codepad.org/4E9Zuz1e
Ich hatte erwartet, um diese zu finden, wenn das Lesen der Titel der Frage, denn die Frage ist nicht etwa der Deklaration von Variablen innerhalb von "Fall:" labels, aber in switch-Anweisungen. Und nur Sie (und VictorH, die Betonung Ihrer Antwort) eigentlich gesprochen Variablen in switch-Anweisungen.
InformationsquelleAutor slim
Den gesamten Abschnitt der Schalter ist eine einzige Erklärung Kontext. Man kann nicht eine variable deklarieren, die in einer case-Anweisung, wie, dass. Versuchen Sie dies:
Corden ich bin zuversichtlich, dass die Initialisierung funktioniert. Haben Sie immer noch behaupten, es kann nicht initialisiert werden?
InformationsquelleAutor
Wenn Ihr code sagt, "int newVal=42", dann würde man vernünftigerweise erwarten, dass newVal ist nie uninitialised. Aber wenn Sie springen über diese Aussage (was du tust) dann ist das genau das, was passiert - newVal ist in-Bereich, hat aber noch nicht zugewiesen wurde.
Wenn das ist, was Sie wirklich passieren sollte dann wird die Sprache benötigt, um es explizit mit den Worten "int newVal; newVal = 42;". Ansonsten können Sie den Umfang der newVal um den einzigen Fall, das ist eher das, was Sie wollte.
Kann es klären sich Dinge, wenn Sie denken, das gleiche Beispiel, aber mit "const int newVal = 42;"
InformationsquelleAutor
Ich wollte nur betonen slim's Punkt. Ein switch-Konstrukt erstellt, einen ganzen erste-Klasse-Bürger Anwendungsbereich. So ist es posible zu deklarieren (und zu initialisieren) einer variable in einer switch-Anweisung vor der ersten case-Bezeichnung, ohne eine zusätzliche Klammer-paar:
die Erklärung
int newVal
wird ausgeführt werden, aber nicht die= 42
Zuordnung.InformationsquelleAutor
Bisher die Antworten wurden für C++.
Für C++, Sie können nicht springen über eine Initialisierung. Sie können in C. Aber in C, eine Deklaration ist keine Anweisung und case-labels zu sein, gefolgt von Anweisungen.
So, gültig (aber hässlich) C, ungültig C++
Conversly, in C++, eine Deklaration ist eine Anweisung, so gilt C++, ungültig C
InformationsquelleAutor Peter
Interessant, dass dies in Ordnung ist:
... aber das ist es nicht:
Ich bekommen, dass ein Update ist einfach genug, aber ich bin nicht zu verstehen, aber warum das erste Beispiel stört den compiler. Wie bereits weiter oben erwähnt wurde (2 Jahre früher hehe), Erklärung ist nicht das, was verursacht die Fehler, die auch trotz der Logik. Initialisierung ist nicht das problem. Wenn die variable initialisiert und deklariert auf den verschiedenen Linien, es kompiliert.
InformationsquelleAutor Dan
Schrieb ich diese Antwort ursprünglich für diese Frage. Aber als ich es beendet hatte, fand ich, dass die Antwort geschlossen wurde. So habe ich es hier gepostet, vielleicht jemand, der gerne Referenzen zu standard wird es hilfreich.
Original-Code in Frage:
Gibt es eigentlich 2 Fragen:
1. Warum kann ich eine variable deklarieren, die nach
case
label?Ist es, weil in C++ - label werden in der form:
N3337 6.1/1
Und in
C++
Erklärung Erklärung wird auch als Anweisung (im Gegensatz zuC
):N3337 6/1:
2. Warum kann ich springen über die Deklaration von Variablen und verwenden Sie es dann?
Weil:
N3337 6.7/3
Seit
k
ist skalaren Typ, und ist nicht initialisiert, an der Stelle der Deklaration springen über es in der Erklärung ist möglich. Dies ist semantisch äquivalent:Jedoch, dass wäre nicht möglich, wenn
x
initialisiert wurde an der Stelle der Erklärung:InformationsquelleAutor PcAF
Neuen Variablen können decalared nur bei block scope. Sie schreiben etwas wie dieses:
Natürlich, newVal nur hat Spielraum innerhalb der geschweiften Klammern...
Cheers, Ralph
InformationsquelleAutor
Einen
switch
block ist nicht das gleiche wie eine Abfolge vonif/else if
Blöcke. Ich bin überrascht, dass keine andere Antwort erklärt es eindeutig.Betrachten Sie diese
switch
Aussage :Kann es überraschend sein, aber der compiler wird es nicht als eine einfache
if/else if
. Es ergibt folgenden code :Den
case
- Anweisungen umgewandelt werden, die Etiketten und rief dann mitgoto
. Die Klammern erstellen einen neuen Bereich, und es ist leicht zu sehen jetzt, warum Sie nicht deklarieren Sie zwei Variablen mit demselben Namen innerhalb einerswitch
block.Kann es seltsam Aussehen, aber es ist notwendig, zu unterstützen fallthrough (das heißt, nicht mit
break
zu lassen, die Ausführung weiter zum nächstencase
).InformationsquelleAutor Dalmas
newVal existiert im gesamten Geltungsbereich der Schalter wird aber nur initialisiert, wenn die VAL des Körpers getroffen wird. Wenn Sie einen block erstellen, um den code in VAL sollte es OK sein.
InformationsquelleAutor marijne
C++ - Standard hat:
Es ist möglich, den transfer in einen block, aber nicht in einer Weise umgeht, die Deklarationen mit Initialisierung. Ein Programm, springt von einem Punkt, wo eine lokale variable mit automatischer Speicherdauer ist nicht im Umfang bis zu einem Punkt, wo es im Rahmen ist schlecht ausgebildet, es sei denn, die variable POD-Typ (3.9) und erklärt wird, ohne eine Initialisierung (8.5).
Den code verdeutlichen diese Regel:
Den code zu zeigen, der Initialisierer-Effekt:
InformationsquelleAutor Jingguo Yao
Ich glaube, das Problem bei der hand ist, wird die Anweisung übersprungen wurde, und Sie versucht, den var anderswo, wäre es nicht deklariert werden.
InformationsquelleAutor William Keller
Scheint es, dass anonyme Objekte kann deklariert werden oder in einer switch-case-Anweisung aus dem Grund, dass Sie können nicht referenziert werden und als solche nicht fallen, die durch das nächste case. Betrachten Sie dieses Beispiel kompiliert auf GCC 4.5.3 und Visual Studio 2008 (möglicherweise ein compliance-Problem tho', so die Experten bitte Wiegen)
keine DV, aber: Die ganze Frage ist zu deklarieren/scope von Variablen bezeichnet. Eine vorübergehende ("anonymes Objekt" ist nicht ein Begriff) ist keine benannte variable, noch ist es eine Erklärung, noch ist es Gegenstand an Umfang (es sei denn, an einer
const
Referenz im Rahmen seiner eigenen). Es ist ein Ausdruck, der lebt und stirbt in seiner Aussage (wo auch immer das sein mag). Es ist daher völlig irrelevant.Foo();
ist nicht die Erklärung, die Frage ist, über Erklärungen.InformationsquelleAutor Olumide