Siempre me he preguntado esto: ¿por qué no puede declarar variables después de una etiqueta de caso en una declaración de cambio? En C ++ puede declarar variables prácticamente en cualquier lugar (y declararlas cerca del primer uso es obviamente algo bueno), pero lo siguiente aún no funcionará:
switch (val)
{
case VAL:
// This won't work
int newVal = 42;
break;
case ANOTHER_VAL:
...
break;
}
Lo anterior me da el siguiente error (MSC):
la inicialización de 'newVal' se omite con la etiqueta 'case'
Esto parece ser una limitación en otros idiomas también. ¿Por qué es este un problema?
c++
switch-statement
Robar
fuente
fuente
Respuestas:
Case
las declaraciones son solo etiquetas . Esto significa que el compilador interpretará esto como un salto directo a la etiqueta. En C ++, el problema aquí es uno de alcance. Sus corchetes definen el alcance como todo dentro de laswitch
declaración. Esto significa que le queda un alcance donde se realizará un salto más en el código omitiendo la inicialización.La forma correcta de manejar esto es definir un alcance específico para esa
case
declaración y definir su variable dentro de ella:fuente
Esta pregunta
seetiquetó originalmente como [C] y [C ++] al mismo tiempo. El código original es de hecho inválido en C y C ++, pero por razones no relacionadas completamente diferentes.En C ++, este código no es válido porque la
case ANOTHER_VAL:
etiqueta salta al alcance de la variable sinnewVal
pasar por su inicialización. Los saltos que omiten la inicialización de objetos automáticos son ilegales en C ++. Este lado del problema se aborda correctamente en la mayoría de las respuestas.Sin embargo, en lenguaje C eludir la inicialización de variables no es un error. Saltar al alcance de una variable sobre su inicialización es legal en C. Simplemente significa que la variable se deja sin inicializar. El código original no se compila en C por una razón completamente diferente. La etiqueta
case VAL:
en el código original se adjunta a la declaración de variablenewVal
. En lenguaje C las declaraciones no son declaraciones. No pueden ser etiquetados. Y esto es lo que causa el error cuando este código se interpreta como código C.Agregar un
{}
bloque adicional corrige los problemas de C ++ y C, a pesar de que estos problemas son muy diferentes. En el lado de C ++, restringe el alcance denewVal
, asegurándose de quecase ANOTHER_VAL:
ya no salta a ese alcance, lo que elimina el problema de C ++. En el lado C, ese extra{}
introduce una declaración compuesta, haciendo que lacase VAL:
etiqueta se aplique a una declaración, lo que elimina el problema de C.En el caso de C, el problema se puede resolver fácilmente sin el
{}
. Simplemente agregue una declaración vacía después de lacase VAL:
etiqueta y el código será válidoTenga en cuenta que aunque ahora es válido desde el punto de vista de C, sigue siendo inválido desde el punto de vista de C ++.
Simétricamente, en el caso de C ++, el problema se puede resolver fácilmente sin el
{}
. Simplemente elimine el inicializador de la declaración de variables y el código será válidoTenga en cuenta que, aunque ahora es válido desde el punto de vista de C ++, sigue siendo inválido desde el punto de vista de C ++.
fuente
newVal
cuándo saltaANOTHER_VAL
?case ANOTHER_VAL:
punto, la variablenewVal
es visible, pero con un valor indeterminado.§A9.3: Compound Statement
de K&R C (segunda edición). La entrada menciona la definición técnica de una declaración compuesta que es{declaration-list[opt] statement-list[opt]}
. Confundido, porque pensé que una declaración era una declaración, lo busqué e inmediatamente encontré esta pregunta, un ejemplo donde dicha disparidad se hace evidente y en realidad rompe un programa. Creo que otra solución (para C) sería poner otra declaración (¿posiblemente una declaración nula?) Antes de la declaración para que se satisfaga la declaración etiquetada .Okay. Solo aclarar esto estrictamente no tiene nada que ver con la declaración. Se relaciona solo con "saltar sobre la inicialización" (ISO C ++ '03 6.7 / 3)
Muchas de las publicaciones aquí han mencionado que saltar sobre la declaración puede dar como resultado que la variable "no se declare". Esto no es verdad. Un objeto POD puede declararse sin un inicializador, pero tendrá un valor indeterminado. Por ejemplo:
Cuando el objeto no es un POD o agregado, el compilador agrega implícitamente un inicializador, por lo que no es posible saltar sobre dicha declaración:
Esta limitación no se limita a la declaración de cambio. También es un error usar 'goto' para saltar sobre una inicialización:
Un poco de trivia es que esta es una diferencia entre C ++ y C. En C, no es un error saltar sobre la inicialización.
Como otros han mencionado, la solución es agregar un bloque anidado para que la vida útil de la variable se limite a la etiqueta del caso individual.
fuente
Toda la declaración de cambio está en el mismo alcance. Para evitarlo, haga esto:
Tenga en cuenta los corchetes.
fuente
Después de leer todas las respuestas y un poco más de investigación, obtengo algunas cosas.
En C, según la especificación,
§6.8.1 Declaraciones etiquetadas:
En C no hay ninguna cláusula que permita una "declaración etiquetada". Simplemente no es parte del lenguaje.
Entonces
Esto no se compilará , consulte http://codepad.org/YiyLQTYw . GCC está dando un error:
Incluso
esto tampoco se está compilando , consulte http://codepad.org/BXnRD3bu . Aquí también recibo el mismo error.
En C ++, según la especificación,
etiquetada-declaración está permitida pero etiquetada -inicialización no está permitida.
Ver http://codepad.org/ZmQ0IyDG .
La solución a tal condición es dos
Utilice un nuevo alcance con {}
O use una declaración ficticia con etiqueta
Declare la variable antes del modificador () e inicialícela con diferentes valores en la declaración de caso si cumple con su requisito
Algunas cosas más con la declaración de cambio
Nunca escriba en el switch ninguna declaración que no forme parte de ninguna etiqueta, porque nunca se ejecutará:
Ver http://codepad.org/PA1quYX3 .
fuente
a
al alcance de la variablea
. Entonces, desde el punto de vista C, el problema está en lacase VAL:
etiqueta y lo describiste correctamente. Pero desde el punto de vista de C ++, el problema es con lacase ANOTHER_VAL:
etiqueta.No puede hacer esto, porque las
case
etiquetas son en realidad solo puntos de entrada en el bloque contenedor.Esto queda más claramente ilustrado por el dispositivo de Duff . Aquí hay un código de Wikipedia:
Observe cómo las
case
etiquetas ignoran totalmente los límites del bloque. Sí, esto es malo. Pero es por eso que su código de ejemplo no funciona. Saltar a unacase
etiqueta es lo mismo que usargoto
, por lo que no puedes saltar sobre una variable local con un constructor.Como lo han indicado varios otros carteles, debe colocar un bloque propio:
fuente
SAR
en x86, versusSHR
cuál es para turnos sin signo).La mayoría de las respuestas hasta ahora son incorrectas en un aspecto: puede declarar variables después de la declaración del caso, pero no puede inicializarlas:
Como se mencionó anteriormente, una buena forma de evitar esto es usar llaves para crear un alcance para su caso.
fuente
Mi truco favorito para cambiar mal es usar un if (0) para saltar una etiqueta de caso no deseada.
Pero muy malvado.
fuente
goto
sin ofuscar el códigoPrueba esto:
fuente
Puede declarar variables dentro de una instrucción switch si inicia un nuevo bloque:
La razón tiene que ver con asignar (y reclamar) espacio en la pila para el almacenamiento de las variables locales.
fuente
Considerar:
En ausencia de declaraciones de interrupción, a veces newVal se declara dos veces, y no sabe si lo hace hasta el tiempo de ejecución. Supongo que la limitación se debe a este tipo de confusión. ¿Cuál sería el alcance de newVal? La convención dictaría que sería todo el bloque de interruptores (entre llaves).
No soy programador de C ++, pero en C:
Funciona bien. Declarar una variable dentro de un bloque de interruptores está bien. Declarar después de un caso de guardia no lo es.
fuente
La sección completa del conmutador es un contexto de declaración única. No puede declarar una variable en una declaración de caso como esa. Intenta esto en su lugar:
fuente
Si su código dice "int newVal = 42", razonablemente esperaría que newVal nunca se inicialice. Pero si pasa por alto esta declaración (que es lo que está haciendo), eso es exactamente lo que sucede: newVal está dentro del alcance pero no se ha asignado.
Si eso es lo que realmente quería que sucediera, entonces el lenguaje requiere hacerlo explícito diciendo "int newVal; newVal = 42;". De lo contrario, puede limitar el alcance de newVal al caso único, que es más probable que desee.
Puede aclarar las cosas si considera el mismo ejemplo pero con "const int newVal = 42;"
fuente
Solo quería enfatizar el punto de Slim . Una construcción de conmutador crea un alcance ciudadano completo de primera clase. Por lo tanto, es posible declarar (e inicializar) una variable en una instrucción switch antes de la primera etiqueta de caso, sin un par de paréntesis adicional:
fuente
int newVal
se ejecutará, pero no la= 42
asignación.Hasta ahora, las respuestas han sido para C ++.
Para C ++, no puede saltar sobre una inicialización. Puede hacerlo en C. Sin embargo, en C, una declaración no es una declaración, y las etiquetas de los casos deben ir seguidas de declaraciones.
Entonces, válido (pero feo) C, inválido C ++
Por el contrario, en C ++, una declaración es una declaración, por lo que lo siguiente es C ++ válido, C inválido
fuente
Interesante que esto esté bien:
... pero esto no es:
Entiendo que una solución es bastante simple, pero aún no entiendo por qué el primer ejemplo no molesta al compilador. Como se mencionó anteriormente (2 años antes, jeje), la declaración no es lo que causa el error, incluso a pesar de la lógica. La inicialización es el problema. Si la variable se inicializa y declara en las diferentes líneas, se compila.
fuente
Escribí esta respuesta originalmente para esta pregunta . Sin embargo, cuando lo terminé, encontré que la respuesta se había cerrado. Así que lo publiqué aquí, tal vez alguien a quien le gusten las referencias al estándar lo encuentre útil.
Código original en cuestión:
En realidad hay 2 preguntas:
1. ¿Por qué puedo declarar una variable después de la
case
etiqueta?Es porque en C ++ la etiqueta tiene que estar en forma:
N3337 6.1 / 1
Y en la
C++
declaración de declaración también se considera como declaración (en oposición aC
):N3337 6/1:
2. ¿Por qué puedo saltar sobre la declaración de variables y luego usarla?
Porque: N3337 6.7 / 3
Dado que
k
es de tipo escalar y no se inicializa en el punto de declaración, es posible saltar sobre ella. Esto es semánticamente equivalente:Sin embargo, eso no sería posible si
x
se inicializara en el punto de declaración:fuente
Las nuevas variables solo se pueden descartar en el alcance del bloque. Necesitas escribir algo como esto:
Por supuesto, newVal solo tiene alcance dentro de las llaves ...
Saludos, Ralph
fuente
Un
switch
bloque no es lo mismo que una sucesión deif/else if
bloques. Me sorprende que ninguna otra respuesta lo explique claramente.Considere esta
switch
afirmación:Puede ser sorprendente, pero el compilador no lo verá como algo simple
if/else if
. Producirá el siguiente código:Las
case
declaraciones se convierten en etiquetas y luego se llaman congoto
. Los corchetes crean un nuevo alcance y es fácil ver ahora por qué no puede declarar dos variables con el mismo nombre dentro de unswitch
bloque.Puede parecer extraño, pero es necesario admitir fallos (es decir, no usar
break
para permitir que la ejecución continúe hasta el siguientecase
).fuente
Creo que el problema en cuestión es que se omitió la declaración e intentó usar la var en otro lugar, no se declararía.
fuente
newVal existe en todo el alcance del conmutador, pero solo se inicializa si se golpea la extremidad VAL. Si crea un bloque alrededor del código en VAL, debería estar bien.
fuente
C ++ Standard tiene: es posible transferir a un bloque, pero no de una manera que omita las declaraciones con la inicialización. Un programa que salta desde un punto donde una variable local con duración de almacenamiento automático no está dentro del alcance hasta un punto donde está dentro del alcance está mal formado a menos que la variable tenga tipo POD (3.9) y se declare sin un inicializador (8.5).
El código para ilustrar esta regla:
El código para mostrar el efecto inicializador:
fuente
Parece que los objetos anónimos se pueden declarar o crear en una declaración de mayúsculas y minúsculas por la razón de que no pueden ser referenciados y, como tales, no pueden pasar al siguiente caso. Considere este ejemplo compila en GCC 4.5.3 y Visual Studio 2008 (podría ser un problema de cumplimiento, por lo que los expertos deben evaluar)
fuente
const
referencia con alcance propio). Es una expresión que vive y muere dentro de su declaración (donde sea que sea). Por lo tanto, es totalmente irrelevante.Foo();
no es una declaración; La pregunta es sobre declaraciones.