Je me suis toujours demandé ceci - pourquoi ne pouvez-vous pas déclarer des variables après une étiquette de cas dans une instruction switch? En C ++, vous pouvez déclarer des variables à peu près n'importe où (et les déclarer proches de la première utilisation est évidemment une bonne chose) mais ce qui suit ne fonctionnera toujours pas:
switch (val)
{
case VAL:
// This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
Ce qui précède me donne l'erreur suivante (MSC):
l'initialisation de «newVal» est ignorée par le libellé «case»
Cela semble également être une limitation dans d'autres langues. Pourquoi est-ce un tel problème?
Réponses:
Case
les instructions ne sont que des étiquettes . Cela signifie que le compilateur interprétera cela comme un saut directement vers l'étiquette. En C ++, le problème ici est celui de la portée. Vos accolades définissent la portée comme tout à l'intérieur duswitch
instruction. Cela signifie que vous vous retrouvez avec une portée où un saut sera effectué plus loin dans le code en sautant l'initialisation.La bonne façon de gérer cela est de définir une portée spécifique à cette
case
instruction et de définir votre variable à l'intérieur:la source
Cette question
esta été étiqueté comme [C] et [C ++] en même temps. Le code d'origine est en effet invalide à la fois en C et C ++, mais pour des raisons totalement différentes et sans rapport.En C ++, ce code n'est pas valide car l'
case ANOTHER_VAL:
étiquette saute dans la portée de la variable ennewVal
contournant son initialisation. Les sauts qui contournent l'initialisation des objets automatiques sont illégaux en C ++. Ce côté du problème est correctement traité par la plupart des réponses.Cependant, en langage C, le contournement de l'initialisation des variables n'est pas une erreur. Sauter dans la portée d'une variable sur son initialisation est légal en C. Cela signifie simplement que la variable n'est pas initialisée. Le code d'origine ne compile pas en C pour une raison complètement différente. L'étiquette
case VAL:
dans le code d'origine est attachée à la déclaration de variablenewVal
. En langage C, les déclarations ne sont pas des déclarations. Ils ne peuvent pas être étiquetés. Et c'est ce qui provoque l'erreur lorsque ce code est interprété comme du code C.Ajout d'un extra
{}
bloc résout à la fois les problèmes C ++ et C, même si ces problèmes sont très différents. Du côté C ++, il restreint la portée denewVal
, en s'assurant quecase ANOTHER_VAL:
ne saute plus dans cette portée, ce qui élimine le problème C ++. Du côté C, ce supplément{}
introduit une instruction composée, ce qui rend l'case VAL:
étiquette à appliquer à une instruction, ce qui élimine le problème C.Dans le cas C, le problème peut être facilement résolu sans
{}
. Ajoutez simplement une instruction vide après l'case VAL:
étiquette et le code deviendra valideNotez que même s'il est désormais valide du point de vue C, il reste invalide du point de vue C ++.
Symétriquement, dans le cas C ++, le problème peut être facilement résolu sans le
{}
. Il suffit de supprimer l'initialiseur de la déclaration de variable et le code deviendra valideNotez que même s'il est désormais valide du point de vue C ++, il reste invalide du point de vue C.
la source
newVal
moment où il sauteANOTHER_VAL
?case ANOTHER_VAL:
point variablenewVal
est visible, mais avec une valeur indéterminée.§A9.3: Compound Statement
dans K&R C (deuxième édition). L'entrée mentionne la définition technique d'une déclaration composée qui est{declaration-list[opt] statement-list[opt]}
. Confus, parce que j'avais pensé qu'une déclaration était une déclaration, je l'ai recherchée et j'ai immédiatement trouvé cette question, un exemple où cette disparité devient apparente et rompt en fait un programme. Je crois qu'une autre solution (pour C) serait de mettre une autre déclaration (éventuellement une déclaration nulle?) Avant la déclaration afin que la déclaration étiquetée soit satisfaite.D'accord. Juste pour clarifier cela, cela n'a strictement rien à voir avec la déclaration. Elle concerne uniquement le "saut par-dessus l'initialisation" (ISO C ++ '03 6.7 / 3)
Beaucoup de messages ici ont mentionné que le fait de sauter la déclaration peut entraîner la non-déclaration de la variable. Ce n'est pas vrai. Un objet POD peut être déclaré sans initialiseur mais il aura une valeur indéterminée. Par exemple:
Lorsque l'objet est un non-POD ou un agrégat, le compilateur ajoute implicitement un initialiseur, et il n'est donc pas possible de passer par-dessus une telle déclaration:
Cette limitation n'est pas limitée à l'instruction switch. C'est aussi une erreur d'utiliser 'goto' pour sauter une initialisation:
Un petit anecdote est que c'est une différence entre C ++ et C. En C, ce n'est pas une erreur de sauter par-dessus l'initialisation.
Comme d'autres l'ont mentionné, la solution consiste à ajouter un bloc imbriqué afin que la durée de vie de la variable soit limitée à l'étiquette de cas individuel.
la source
L'instruction switch entière est dans la même portée. Pour le contourner, procédez comme suit:
Notez les crochets.
la source
Après avoir lu toutes les réponses et quelques recherches supplémentaires, je reçois quelques choses.
En C, selon la spécification,
§6.8.1 Déclarations étiquetées:
En C, aucune clause ne permet une "déclaration étiquetée". Cela ne fait tout simplement pas partie de la langue.
Donc
Cela ne se compilera pas , voir http://codepad.org/YiyLQTYw . GCC donne une erreur:
Même
ce n'est pas non plus la compilation , voir http://codepad.org/BXnRD3bu . Ici, je reçois également la même erreur.
En C ++, selon la spécification,
La déclaration étiquetée est autorisée mais l'initialisation étiquetée n'est pas autorisée.
Voir http://codepad.org/ZmQ0IyDG .
La solution à une telle condition est deux
Soit utiliser une nouvelle portée en utilisant {}
Ou utilisez une déclaration fictive avec une étiquette
Déclarez la variable avant switch () et initialisez-la avec différentes valeurs dans l'instruction case si elle répond à vos besoins
Encore plus de choses avec l'instruction switch
N'écrivez jamais dans le commutateur des instructions qui ne font partie d'aucune étiquette, car elles ne seront jamais exécutées:
Voir http://codepad.org/PA1quYX3 .
la source
a
dans la portée de la variablea
. Donc, du point de vue C, le problème vient de l'case VAL:
étiquette et vous l'avez décrite correctement. Mais du point de vue C ++, le problème est avec l'case ANOTHER_VAL:
étiquette.Vous ne pouvez pas faire cela, car les
case
étiquettes ne sont en fait que des points d'entrée dans le bloc conteneur.Ceci est plus clairement illustré par l'appareil de Duff . Voici un code de Wikipedia:
Remarquez comment les
case
étiquettes ignorent totalement les limites des blocs. Oui, c'est mal. Mais c'est pourquoi votre exemple de code ne fonctionne pas. Passer à unecase
étiquette équivaut à utilisergoto
, vous n'êtes donc pas autorisé à passer par-dessus une variable locale avec un constructeur.Comme plusieurs autres affiches l'ont indiqué, vous devez mettre votre propre bloc:
la source
SAR
en x86, contreSHR
lequel correspond aux décalages non signés).Jusqu'à présent, la plupart des réponses sont fausses sur un point: vous pouvez déclarer des variables après l'instruction case, mais vous ne pouvez pas les initialiser:
Comme mentionné précédemment, une bonne solution consiste à utiliser des accolades pour créer une portée pour votre cas.
la source
Mon astuce maléfique préférée est d'utiliser un if (0) pour sauter une étiquette de cas indésirable.
Mais très mal.
la source
goto
sansEssaye ça:
la source
Vous pouvez déclarer des variables dans une instruction switch si vous démarrez un nouveau bloc:
La raison est liée à l'allocation (et la récupération) d'espace sur la pile pour le stockage des variables locales.
la source
Considérer:
En l'absence d'instructions break, newVal est parfois déclaré deux fois, et vous ne savez pas si c'est le cas jusqu'à l'exécution. Je suppose que la limitation est due à ce genre de confusion. Quelle serait la portée de newVal? La convention dicterait que ce serait l'ensemble du bloc de commutation (entre les accolades).
Je ne suis pas programmeur C ++, mais en C:
Fonctionne bien. Déclarer une variable à l'intérieur d'un bloc de commutation est très bien. Déclarer après un garde de cas ne l'est pas.
la source
La section entière du commutateur est un contexte de déclaration unique. Vous ne pouvez pas déclarer une variable dans une instruction case comme celle-ci. Essayez plutôt ceci:
la source
Si votre code dit "int newVal = 42", vous vous attendez raisonnablement à ce que newVal ne soit jamais non initialisé. Mais si vous passez au-dessus de cette déclaration (ce que vous faites), c'est exactement ce qui se passe - newVal est dans le champ d'application mais n'a pas été attribué.
Si c'est ce que vous vouliez vraiment faire, le langage nécessite de le rendre explicite en disant "int newVal; newVal = 42;". Sinon, vous pouvez limiter la portée de newVal au cas unique, ce qui correspond plus probablement à ce que vous vouliez.
Cela peut clarifier les choses si vous considérez le même exemple mais avec "const int newVal = 42;"
la source
Je voulais juste souligner mince de points . Une construction de commutateur crée une portée entière de premier ordre pour les citoyens. Il est donc possible de déclarer (et d'initialiser) une variable dans une instruction switch avant la première étiquette de cas, sans paire de crochets supplémentaire:
la source
int newVal
sera exécutée, mais pas l'= 42
affectation.Jusqu'à présent, les réponses ont été pour C ++.
Pour C ++, vous ne pouvez pas sauter une initialisation. Vous pouvez le faire en C. Cependant, en C, une déclaration n'est pas une instruction, et les étiquettes de cas doivent être suivies d'instructions.
Donc, C valide (mais moche), C ++ invalide
Inversement, en C ++, une déclaration est une instruction, donc ce qui suit est C ++ valide, C non valide
la source
Intéressant que ce soit bien:
... mais ce n'est pas:
Je comprends qu'un correctif est assez simple, mais je ne comprends pas encore pourquoi le premier exemple ne dérange pas le compilateur. Comme évoqué précédemment (2 ans plus tôt hehe), la déclaration n'est pas la cause de l'erreur, même malgré la logique. L'initialisation est le problème. Si la variable est initialisée et déclarée sur les différentes lignes, elle se compile.
la source
J'ai écrit cette réponse à l'origine pour cette question . Cependant, quand j'ai fini, j'ai trouvé que la réponse était fermée. Je l'ai donc posté ici, peut-être que quelqu'un qui aime les références à la norme le trouvera utile.
Code original en question:
Il y a en fait 2 questions:
1. Pourquoi puis-je déclarer une variable après l'
case
étiquette?C'est parce que dans C ++, l'étiquette doit être en forme:
N3337 6.1 / 1
Et dans la
C++
déclaration, la déclaration est également considérée comme une déclaration (par opposition àC
):N3337 6/1:
2. Pourquoi puis-je sauter par-dessus la déclaration de variable et ensuite l'utiliser?
Parce que: N3337 6.7 / 3
Depuis
k
est de type scalaire , et n'est pas initialisé au moment de la déclaration, il est possible de sauter par-dessus. C'est sémantiquement équivalent:Cependant cela ne serait pas possible, si a
x
été initialisé au moment de la déclaration:la source
Les nouvelles variables peuvent être décalarisées uniquement à la portée du bloc. Vous devez écrire quelque chose comme ceci:
Bien sûr, newVal n'a de portée que dans les accolades ...
À la vôtre, Ralph
la source
Un
switch
bloc n'est pas la même chose qu'une succession deif/else if
blocs. Je suis surpris qu'aucune autre réponse ne l'explique clairement.Considérez cette
switch
déclaration:Cela peut être surprenant, mais le compilateur ne le considérera pas comme un simple
if/else if
. Il produira le code suivant:Les
case
instructions sont converties en étiquettes, puis appelées avecgoto
. Les crochets créent une nouvelle portée et il est facile de voir maintenant pourquoi vous ne pouvez pas déclarer deux variables avec le même nom dans unswitch
bloc.Cela peut sembler étrange, mais il est nécessaire de prendre en charge la fonction fallthrough (c'est-à-dire de ne pas utiliser
break
pour laisser l'exécution continuer jusqu'à la suivantecase
).la source
Je crois que le problème est que la déclaration a été ignorée et que vous avez essayé d'utiliser le var ailleurs, elle ne serait pas déclarée.
la source
newVal existe dans toute l'étendue du commutateur mais n'est initialisé que si le membre VAL est touché. Si vous créez un bloc autour du code dans VAL, cela devrait être OK.
la source
C ++ Standard a: Il est possible de transférer dans un bloc, mais pas d'une manière qui contourne les déclarations avec initialisation. Un programme qui passe d'un point où une variable locale avec une durée de stockage automatique n'est pas dans la portée à un point où elle est dans la portée est mal formé sauf si la variable a le type POD (3.9) et est déclarée sans initialiseur (8.5).
Le code pour illustrer cette règle:
Le code pour montrer l'effet d'initialisation:
la source
Il semble que des objets anonymes peuvent être déclarés ou créés dans une instruction de cas de commutateur pour la raison qu'ils ne peuvent pas être référencés et en tant que tels ne peuvent pas passer au cas suivant. Considérez que cet exemple se compile sur GCC 4.5.3 et Visual Studio 2008 (cela pourrait être un problème de conformité, alors les experts doivent peser)
la source
const
référence avec une portée propre). C'est une expression qui vit et meurt dans sa déclaration (où que ce soit). Par conséquent, c'est totalement hors de propos.Foo();
n'est pas une déclaration; la question concerne les déclarations.