¿Por qué no se pueden declarar variables en una declaración de cambio?

945

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?

Robar
fuente
10
Para obtener una explicación basada en la gramática de C BNF, consulte stackoverflow.com/questions/1180550/weird-switch-error-in-obj-c/…
johne
Aquí hay una muy buena lectura sobre las declaraciones de cambio y las etiquetas (ABC :) en general.
Etherealone
44
Yo diría '¿Por qué no se pueden inicializar las variables en una declaración de cambio en lugar de declararlas? Ya que solo declarar la variable solo me da una advertencia en MSVC.
ZoomIn

Respuestas:

1144

Caselas 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 la switchdeclaració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 casedeclaración y definir su variable dentro de ella:

switch (val)
{   
case VAL:  
{
  // This will work
  int newVal = 42;  
  break;
}
case ANOTHER_VAL:  
...
break;
}
TJ Seabrooks
fuente
94
En relación con la apertura de un nuevo alcance, favorezca la legibilidad y la coherencia en el código. En los viejos tiempos, es posible que haya obtenido automáticamente un marco de pila "extra", pero ahora ese no debería ser el caso para ningún compilador de optimización decente.
Alto Jeff
10
Estoy de acuerdo con Jeff: es muy fácil "asumir" el alcance al leer una declaración de cambio debido al estilo de sangría que usa la mayoría de las personas. Mi propio estilo es abrir siempre un nuevo ámbito para cada caso / predeterminado si tiene más de una línea de largo.
Ofertas del
39
workmad3 - ¿Puede encontrarme algún compilador de C ++ que genere un nuevo marco de pila si no declara ninguna variable nueva? Me preocupó brevemente, pero ninguno de G ++ 3.1, Visual C ++ 7 o Intel C ++ 8 generará ningún código para nuevos ámbitos en los que no declare ninguna variable.
Chris Jefferson
10
@ workmad3 al ingresar un nuevo bloque de llaves no causa un nuevo marco de pila stackoverflow.com/questions/2759371/…
MTVS
3
@TallJef No sé a qué 'viejos días' te refieres. Nunca he encontrado un compilador donde no se asigna todo el espacio de pila para un método cuando se ingresa el método, en 40 años.
Marqués de Lorne
333

Esta pregunta se etiquetó 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 sin newValpasar 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 variable newVal. 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.

    switch (val)  
    {  
    case VAL:             /* <- C error is here */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:     /* <- C++ error is here */
      ...
      break;
    }
    

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 de newVal, asegurándose de que case 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 la case 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 la case VAL:etiqueta y el código será válido

    switch (val)  
    {  
    case VAL:;            /* Now it works in C! */
      int newVal = 42;  
      break;
    case ANOTHER_VAL:  
      ...
      break;
    }
    

    Tenga 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álido

    switch (val)  
    {  
    case VAL: 
      int newVal;
      newVal = 42;  
      break;
    case ANOTHER_VAL:     /* Now it works in C++! */
      ...
      break;
    }
    

    Tenga 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 ++.

Hormiga
fuente
44
@AnT: entiendo por qué el que corrige C ++ no es aplicable para C; sin embargo, no puedo entender cómo soluciona el problema de C ++ de omitir la inicialización en primer lugar. ¿Todavía no se saltaría la declaración y la asignación de newValcuándo salta ANOTHER_VAL?
legends2k
13
@ legends2k: Sí, todavía lo omite. Sin embargo, cuando digo "soluciona el problema", quiero decir que corrige el error del compilador de C ++ . En C ++ es ilegal omitir una declaración escalar con inicializador , pero está perfectamente bien omitir una declaración escalar sin inicializador . En el case ANOTHER_VAL:punto, la variable newVales visible, pero con un valor indeterminado.
ANT
3
Fascinante. Encontré esta pregunta después de leer §A9.3: Compound Statementde 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 .
Braden Best
Vaya, acabo de notar que la solución de declaración nula que sugerí ya está en tu respuesta. Olvidalo entonces.
Braden Best
3
Vale la pena señalar que la solución de agregar una declaración vacía solo funciona para C99 en adelante. En C89, las variables deben declararse al comienzo de su bloque de inclusión.
Arthur Tacca
136

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:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' initialized to 0, but this statement
            // is jumped when 'i == 1'
     break;
   case 1:
     ++j;   // 'j' is in scope here - but it has an indeterminate value
     break;
}

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:

class A {
public:
  A ();
};

switch (i)  // Error - jumping over initialization of 'A'
{
   case 0:
     A j;   // Compiler implicitly calls default constructor
     break;
   case 1:
     break;
}

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:

goto LABEL;    // Error jumping over initialization
int j = 0; 
LABEL:
  ;

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.

Richard Corden
fuente
2
"Error al saltar sobre la inicialización" ??? No con mi CCG. Puede dar una advertencia de "j puede usarse unificado" cuando se usa j debajo de la etiqueta, pero no hay error. Sin embargo, en caso de cambio, hay un error (un error grave, no una advertencia débil).
Mecki
99
@Mecki: es ilegal en C ++. ISO C ++ '03 - 6.7 / 3: "... Un programa que salta desde un punto donde una variable local con duración de almacenamiento automática no está dentro del alcance hasta un punto donde está dentro del alcance está mal formado a menos que la variable tenga el tipo POD (3.9) y se declara sin un inicializador (8.5) ".
Richard Corden
1
Sí, pero no es ilegal en C (al menos gcc dice que no lo es). j no se inicializará (tendrá algún número aleatorio), pero el compilador lo compila. Sin embargo, en el caso de la instrucción switch, el compilador ni siquiera la compilará y no veo la diferencia entre un caso goto / label y un caso switch.
Mecki
8
@Mecki: en general, el comportamiento de un solo compilador no refleja necesariamente lo que el lenguaje realmente permite. He comprobado tanto C'90 como C'99 y ambos estándares incluyen un ejemplo con un salto sobre la inicialización en una declaración de cambio.
Richard Corden
38

Toda la declaración de cambio está en el mismo alcance. Para evitarlo, haga esto:

switch (val)
{
    case VAL:
    {
        // This **will** work
        int newVal = 42;
    }
    break;

    case ANOTHER_VAL:
      ...
    break;
}

Tenga en cuenta los corchetes.

Mark Ingram
fuente
30

Después de leer todas las respuestas y un poco más de investigación, obtengo algunas cosas.

Case statements are only 'labels'

En C, según la especificación,

§6.8.1 Declaraciones etiquetadas:

labeled-statement:
    identifier : statement
    case constant-expression : statement
    default : statement

En C no hay ninguna cláusula que permita una "declaración etiquetada". Simplemente no es parte del lenguaje.

Entonces

case 1: int x=10;
        printf(" x is %d",x);
break;

Esto no se compilará , consulte http://codepad.org/YiyLQTYw . GCC está dando un error:

label can only be a part of statement and declaration is not a statement

Incluso

  case 1: int x;
          x=10;
            printf(" x is %d",x);
    break;

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

  1. Utilice un nuevo alcance con {}

    case 1:
           {
               int x=10;
               printf(" x is %d", x);
           }
    break;
  2. O use una declaración ficticia con etiqueta

    case 1: ;
               int x=10;
               printf(" x is %d",x);
    break;
  3. Declare la variable antes del modificador () e inicialícela con diferentes valores en la declaración de caso si cumple con su requisito

    main()
    {
        int x;   // Declare before
        switch(a)
        {
        case 1: x=10;
            break;
    
        case 2: x=20;
            break;
        }
    }

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á:

switch(a)
{
    printf("This will never print"); // This will never executed

    case 1:
        printf(" 1");
        break;

    default:
        break;
}

Ver http://codepad.org/PA1quYX3 .

Jeegar Patel
fuente
2
Usted describió correctamente el problema de C. Pero la afirmación de que en C ++ etiquetada la inicialización no está permitida no es completamente cierta. No hay nada malo con la inicialización etiquetada en C ++. Lo que C ++ no permite es saltar sobre la inicialización de la variable aal alcance de la variable a. Entonces, desde el punto de vista C, el problema está en la case VAL:etiqueta y lo describiste correctamente. Pero desde el punto de vista de C ++, el problema es con la case ANOTHER_VAL:etiqueta.
ANT
En C ++, a diferencia de C, las declaraciones son un subconjunto de declaraciones.
Keith Thompson, el
20

No puede hacer esto, porque las caseetiquetas 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:

strcpy(char *to, char *from, size_t count) {
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}

Observe cómo las caseetiquetas 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 una caseetiqueta es lo mismo que usar goto, 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:

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break; 
    }
    ...
 }
emk
fuente
1
La implementación de este dispositivo Duff tiene un error que lo hace extremadamente lento: el recuento es de tipo int, por lo que el% debe realizar una operación de división / módulo real. Haga el recuento sin firmar (o mejor aún, use siempre size_t para recuentos / índices) y el problema desaparecerá.
R .. GitHub DEJA DE AYUDAR A ICE
1
@R ..: ¿Qué? En el sistema de complemento a dos, la firma no afecta a los módulos por potencias de 2 (es solo un AND en los bits inferiores), y no afecta a las divisiones por potencias de 2 siempre que la arquitectura de su procesador tenga una operación aritmética de desplazamiento a la derecha ( SARen x86, versus SHRcuál es para turnos sin signo).
Chris Jester-Young
@ Chris: Creo que quiere decir cuando el compilador debe permitir valores negativos donde "solo un AND en los bits inferiores" no se cumple; por ejemplo, -1% 8 da -1 en el sistema de complemento de este dos usando g ++ (el signo en este caso es la implementación definida por 5.6 / 4).
3
@ Chris: Estoy de acuerdo con usted en que R está exagerando el impacto; Solo vi tu comentario y supe que un simple Y no era suficiente.
1
También vale la pena señalar que el código original de Wikipedia es para enviar datos a una salida mapeada en memoria, lo que parece extraño aquí porque no se menciona y cada byte se copia en la misma ubicación "a". Podría evitar eso agregando postfix ++ al to, o mencionando que el caso de uso es para IO mapeado en memoria. Totalmente periférico a la pregunta original :-).
Peter
16

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:

case 1:
    int x; // Works
    int y = 0; // Error, initialization is skipped by case
    break;
case 2:
    ...

Como se mencionó anteriormente, una buena forma de evitar esto es usar llaves para crear un alcance para su caso.

MrZebra
fuente
1
Sr. 32, ha entendido mal cuál es su error: sí, eso no se va a compilar, pero no porque esté declarando una variable dentro de un interruptor. El error se debe a que está intentando declarar una variable después de una declaración, lo cual es ilegal en C.
MrZebra
1
Ahora un día que es legal en c90 y una versión más nueva de c
Jeegar Patel
12

Mi truco favorito para cambiar mal es usar un if (0) para saltar una etiqueta de caso no deseada.

switch(val)
{
case 0:
// Do something
if (0) {
case 1:
// Do something else
}
case 2:
// Do something in all cases
}

Pero muy malvado.

Jeremy
fuente
Muy agradable. Ejemplo de por qué: el caso 0 y el caso 1 podrían, por ejemplo, inicializar una variable diferente que luego se usa en el caso 2.
hlovdal
1
Si desea que tanto el caso 0 como el caso 1 caigan en el caso 2. (sin que el caso 0 caiga en el caso 1). No sé si es realmente útil, pero seguro que funciona.
Petruza
1
Puede saltar a la etiqueta requerida gotosin ofuscar el código
SomeWittyUsername
10

Prueba esto:

switch (val)
{
    case VAL:
    {
        int newVal = 42;
    }
    break;
}
Escudo dan
fuente
7

Puede declarar variables dentro de una instrucción switch si inicia un nuevo bloque:

switch (thing)
{ 
  case A:
  {
    int i = 0;  // Completely legal
  }
  break;
}

La razón tiene que ver con asignar (y reclamar) espacio en la pila para el almacenamiento de las variables locales.

Seb Rose
fuente
1
La variable se puede declarar, pero no se puede inicializar. Además, estoy bastante seguro de que el problema no se relaciona de ninguna manera con la pila y las variables locales.
Richard Corden
6

Considerar:

switch(val)
{
case VAL:
   int newVal = 42;
default:
   int newVal = 23;
}

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:

switch(val) {
    int x;
    case VAL:
        x=1;
}

Funciona bien. Declarar una variable dentro de un bloque de interruptores está bien. Declarar después de un caso de guardia no lo es.

Delgado
fuente
3
@ Mr.32: en realidad su ejemplo muestra que un printf no se ejecuta, pero en este caso, int x no es una declaración sino una declaración, se declara la x, se reserva espacio cada vez que se apila el entorno de la función, ver: codepad.org/4E9Zuz1e
Petruza
Esperaba encontrar esto al leer el título de la pregunta, porque la pregunta no se trata de declarar variables dentro de las etiquetas "case:", sino en declaraciones de cambio. Y solo usted (y VictorH, enfatizando su respuesta) realmente habló sobre las variables en las declaraciones de cambio.
cesss
4

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:

switch (val)  
{  
case VAL:
{
  // This will work
  int newVal = 42;
  break;
}
case ANOTHER_VAL:  
  ...
  break;
}

fuente
La variable se puede declarar, pero no se puede inicializar.
Richard Corden
@ Richard Corden Estoy seguro de que la inicialización funcionará. ¿Sigues afirmando que no se puede inicializar?
chux
3

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
3

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:

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}
VictorH
fuente
-1 aquí int newVal = 42; nunca será ejecutado. ver este codepad.org/PA1quYX3
Jeegar Patel
44
la declaración int newVal se ejecutará, pero no la = 42asignación.
Petruza
3

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 ++

switch (something)
{
  case 1:; // Ugly hack empty statement
    int i = 6;
    do_stuff_with_i(i);
    break;
  case 2:
    do_something();
    break;
  default:
    get_a_life();
}

Por el contrario, en C ++, una declaración es una declaración, por lo que lo siguiente es C ++ válido, C inválido

switch (something)
{
  case 1:
    do_something();
    break;
  case 2:
    int i = 12;
    do_something_else();
}
Peter
fuente
1
El segundo ejemplo NO es válido C ++ (prueba con vc2010 y gcc 4.6.1 C ++ no permite omitir la parte de inicialización. El mensaje de error de gcc es: inicialización cruzada de 'int i'
zhaorufei
3

Interesante que esto esté bien:

switch (i)  
{  
case 0:  
    int j;  
    j = 7;  
    break;  

case 1:  
    break;
}

... pero esto no es:

switch (i)  
{  
case 0:  
    int j = 7;  
    break;  

case 1:  
    break;
}

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.

Dan
fuente
1
Primero no está bien en gcc 4.2: "error: expresión esperada antes de 'int'". Como Peter y Mr.32 dicen, "caso 0:; int j; ..." y "caso 0:; int j = 7; ..." funcionan ambos. El problema en C es simplemente que "case <label>: declaración" no es una sintaxis C válida.
dubiousjim
3

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:

int i;
i = 2;
switch(i)
{
    case 1: 
        int k;
        break;
    case 2:
        k = 1;
        cout<<k<<endl;
        break;
}

En realidad hay 2 preguntas:

1. ¿Por qué puedo declarar una variable después de la caseetiqueta?

Es porque en C ++ la etiqueta tiene que estar en forma:

N3337 6.1 / 1

declaración-etiquetada:

...

  • atributo-especificador-seqopt case constant-expression :statement

...

Y en la C++ declaración de declaración también se considera como declaración (en oposición a C):

N3337 6/1:

declaración :

...

Declaración

...

2. ¿Por qué puedo saltar sobre la declaración de variables y luego usarla?

Porque: N3337 6.7 / 3

Es posible transferir a un bloque, pero no de una manera que omita las declaraciones con la inicialización . Un programa que salta (La transferencia de la condición de una declaración de cambio a una etiqueta de caso se considera un salto a este respecto).

desde un punto donde una variable con duración de almacenamiento automático no está dentro del alcance hasta un punto donde está dentro del alcance, está mal formada a menos que la variable tenga un tipo escalar , un tipo de clase con un constructor trivial predeterminado y un destructor trivial, una versión calificada para cv de uno de estos tipos, o una matriz de uno de los tipos anteriores y se declara sin un inicializador (8.5).

Dado que kes de tipo escalar y no se inicializa en el punto de declaración, es posible saltar sobre ella. Esto es semánticamente equivalente:

goto label;

int x;

label:
cout << x << endl;

Sin embargo, eso no sería posible si xse inicializara en el punto de declaración:

 goto label;

    int x = 58; //error, jumping over declaration with initialization

    label:
    cout << x << endl;
PcAF
fuente
1

Las nuevas variables solo se pueden descartar en el alcance del bloque. Necesitas escribir algo como esto:

case VAL:  
  // This will work
  {
  int newVal = 42;  
  }
  break;

Por supuesto, newVal solo tiene alcance dentro de las llaves ...

Saludos, Ralph


fuente
1

Un switchbloque no es lo mismo que una sucesión de if/else ifbloques. Me sorprende que ninguna otra respuesta lo explique claramente.

Considere esta switchafirmación:

switch (value) {
    case 1:
        int a = 10;
        break;
    case 2:
        int a = 20;
        break;
}

Puede ser sorprendente, pero el compilador no lo verá como algo simple if/else if. Producirá el siguiente código:

if (value == 1)
    goto label_1;
else if (value == 2)
    goto label_2;
else
    goto label_end;

{
label_1:
    int a = 10;
    goto label_end;
label_2:
    int a = 20; // Already declared !
    goto label_end;
}

label_end:
    // The code after the switch block

Las casedeclaraciones se convierten en etiquetas y luego se llaman con goto. 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 un switchbloque.

Puede parecer extraño, pero es necesario admitir fallos (es decir, no usar breakpara permitir que la ejecución continúe hasta el siguiente case).

Dalmas
fuente
0

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.

William Keller
fuente
0

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.

marijne
fuente
0

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:

#include <iostream>

using namespace std;

class X {
  public:
    X() 
    {
     cout << "constructor" << endl;
    }
    ~X() 
    {
     cout << "destructor" << endl;
    }
};

template <class type>
void ill_formed()
{
  goto lx;
ly:
  type a;
lx:
  goto ly;
}

template <class type>
void ok()
{
ly:
  type a;
lx:
  goto ly;
}

void test_class()
{
  ok<X>();
  // compile error
  ill_formed<X>();
}

void test_scalar() 
{
  ok<int>();
  ill_formed<int>();
}

int main(int argc, const char *argv[]) 
{
  return 0;
}

El código para mostrar el efecto inicializador:

#include <iostream>

using namespace std;

int test1()
{
  int i = 0;
  // There jumps fo "case 1" and "case 2"
  switch(i) {
    case 1:
      // Compile error because of the initializer
      int r = 1; 
      break;
    case 2:
      break;
  };
}

void test2()
{
  int i = 2;
  switch(i) {
    case 1:
      int r;
      r= 1; 
      break;
    case 2:
      cout << "r: " << r << endl;
      break;
  };
}

int main(int argc, const char *argv[]) 
{
  test1();
  test2();
  return 0;
}
Jingguo Yao
fuente
0

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)

#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

    switch( i )
    {
    case 42:
        Foo();  // Apparently valid
        break;

    default:
        break;
    }
    return EXIT_SUCCESS;
}
Olumida
fuente
Si va a rechazarlo, explique por qué. Tengo curiosidad por saber por qué crear un objeto anónimo parece ser una excepción.
Olumide
1
no es un DV, pero: toda la cuestión es sobre la declaración / alcance de las variables con nombre. Un temporal ("objeto anónimo" no es un término) no es una variable con nombre, ni es una declaración, ni está sujeto a alcance (a menos que esté vinculado a una constreferencia 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.
underscore_d
Foo();no es una declaración; La pregunta es sobre declaraciones.
MM