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

1027

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

initialization of 'newVal' is skipped by 'case' label

Esto también parece ser una limitación en otros idiomas. ¿Por qué es esto un problema tan grande?

4
  • 10
    Para obtener una explicación basada en la gramática C BNF, consulte stackoverflow.com/questions/1180550/weird-switch-error-in-obj-c/…
    johne
    12 de enero de 2010 a las 3:30
  • Aquí hay una muy buena lectura sobre las declaraciones y etiquetas de cambio (ABC :) en general. 16/08/12 a las 22:33
  • 5
    Yo diría '¿Por qué no se pueden inicializar las variables en una declaración de cambio en lugar de declarar?' Ya que simplemente declarar la variable solo me da una advertencia en MSVC.
    ZoomIn
    9 de diciembre de 2013 a las 6:21
  • 1
    Si pones todo dentro de la etiqueta del caso entre llaves {}, funcionará.
    E Purdy
    27/08/20 a las 13:33
1244

CaseLas declaraciones son solo etiquetas . Esto significa que el compilador interpretará esto como un salto directamente a la etiqueta. En C ++, el problema aquí es de alcance. Sus llaves definen el alcance como todo lo que está dentro de la switchdeclaración. Esto significa que se queda con un alcance en el que 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;
}
10
  • 95
    En relación con la apertura de un nuevo alcance, favorezca la legibilidad y la coherencia en el código. En los viejos tiempos, podría haber 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. 18 de septiembre de 2008 a las 14:37
  • 11
    Estoy de acuerdo con Jeff: es muy fácil "asumir" el alcance cuando se lee una declaración de cambio debido al estilo de sangría que usa la mayoría de la gente. Mi propio estilo es siempre abrir un nuevo alcance para cada caso / predeterminado si tiene más de una línea de longitud.
    Bids
    31 de ene. De 2009 a las 16:43
  • 40
    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. 7 feb 09 a las 20:11
  • 10
    @ workmad3 al ingresar un nuevo bloque de llaves no genera un nuevo marco de pila stackoverflow.com/questions/2759371/…
    MTVS
    14 de enero de 2013 a las 9:40
  • 3
    @TallJef No sé a qué "viejos tiempos" te refieres. Nunca me he encontrado con 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. 10/04/2017 a las 8:05
390

Esta pregunta se etiquetó originalmente como [C] y [C ++] al mismo tiempo. De hecho, el código original no es válido tanto en C como en C ++, pero por razones completamente diferentes y no relacionadas.

  • En C ++, este código no es válido porque la case ANOTHER_VAL:etiqueta entra en el ámbito de la variable sin newValpasar por su inicialización. Los saltos que omiten la inicialización de objetos automáticos son ilegales en C ++. La mayoría de las respuestas abordan correctamente este aspecto del problema.

  • Sin embargo, en lenguaje C, omitir 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 se pueden etiquetar. Y esto es lo que provoca 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, aunque estos problemas son muy diferentes. En el lado de C ++, restringe el alcance de newVal, asegurándose de que case ANOTHER_VAL:ya no salte a ese alcance, lo que elimina el problema de C ++. En el lado C, ese extra {}introduce una declaración compuesta, lo que hace que la case VAL:etiqueta se aplique a una declaración, lo que elimina el problema C.

  • En el caso 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 variable 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.

8
  • 5
    @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. ¿No se saltaría la declaración y la asignación de newValcuándo salta ANOTHER_VAL? 19/06/2015 a las 9:08
  • 14
    @ legends2k: Sí, todavía lo omite. Sin embargo, cuando digo "soluciona el problema", me refiero a 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 valor indeterminado.
    AnT
    19/06/2015 a las 14:45
  • 3
    Fascinante. Encontré esta pregunta después de leer §A9.3: Compound Statementde K&R C (segunda edición). La entrada mencionaba la definición técnica de una declaración compuesta que es {declaration-list[opt] statement-list[opt]}. Confundido, porque había pensado que una declaración ERA una declaración, la busqué e inmediatamente encontré esta pregunta, un ejemplo en el que dicha disparidad se hace evidente y realmente 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 . 4 de febrero de 2017 a las 10:04
  • 1
    Vaya, acabo de notar que la solución de declaración nula que sugerí ya está en su respuesta. Olvidalo entonces. 4 de febrero de 2017 a las 10:19
  • 4
    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 adjunto. 12/12/19 a las 16:40
138

Está bien. 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 la declaración puede resultar en que la variable "no se declare". Esto no es verdad. Un objeto POD se puede declarar sin un inicializador pero tendrá un valor indeterminado. Por ejemplo:

switch (i)
{
   case 0:
     int j; // 'j' has indeterminate value
     j = 0; // 'j' set (not 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 un 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 han mencionado otros, la solución es agregar un bloque anidado para que la vida útil de la variable se limite a la etiqueta de caso individual.

4
  • 2
    "Error al saltar sobre la inicialización" ??? No con mi GCC. 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
    20 de febrero de 2010 a las 1:20
  • 9
    @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ático no está dentro del alcance a 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) ". 22/02/10 a las 18:21
  • 1
    Sí, pero no es ilegal en C (al menos gcc dice que no lo es). j no estará inicializado (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
    22/02/10 a las 22:01
  • 8
    @Mecki: en general, el comportamiento de un solo compilador no refleja necesariamente lo que realmente permite el lenguaje. 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. 28/02/10 a las 22:23
39

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

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

    case ANOTHER_VAL:
      ...
    break;
}

Tenga en cuenta los corchetes.

0
32

Después de leer todas las respuestas e investigar un poco más, 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 idioma.

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 compila , consulte http://codepad.org/BXnRD3bu . Aquí también obtengo el mismo error.


En C ++, según la especificación,

La declaración etiquetada está permitida pero la inicialización etiquetada no está permitida.

Consulte 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 de switch () 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 declaraciones en el conmutador que no formen parte de ninguna etiqueta, porque nunca se ejecutarán:

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

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

    default:
        break;
}

Consulte http://codepad.org/PA1quYX3 .

2
  • 2
    Describiste correctamente el problema de C. Pero la afirmación de que en C ++ la inicialización etiquetada no está permitida es completamente falsa. No hay nada de malo en la inicialización etiquetada en C ++. Lo que C ++ no permite es saltar la inicialización de la variable aal alcance de la variable a. Entonces, desde el punto de vista de C, el problema está con la case VAL:etiqueta y la describiste correctamente. Pero desde el punto de vista de C ++, el problema está en la case ANOTHER_VAL:etiqueta.
    AnT
    7 de noviembre de 2013 a las 8:14
  • En C ++, a diferencia de C, las declaraciones son un subconjunto de declaraciones. 7 de agosto de 2016 a las 0:01
20

No puede hacer esto, porque las caseetiquetas son en realidad solo puntos de entrada al bloque contenedor.

Esto se ilustra más claramente en 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 malvado. Pero esta es la razón por la que su ejemplo de código no funciona. Saltar a una caseetiqueta es lo mismo que usar goto, por lo que no se le permite saltar sobre una variable local con un constructor.

Como han indicado varios otros carteles, debe colocar un bloque propio:

switch (...) {
    case FOO: {
        MyObject x(...);
        ...
        break; 
    }
    ...
 }
9
  • 1
    La implementación de este dispositivo de Duff tiene un error que lo hace extremadamente lento: count es de tipo int, por lo que% 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á. 3/07/10 a las 20:58
  • 1
    @R ..: ¡¿Qué ?! En un sistema de complemento a dos, la firma no afecta los módulos por potencias de 2 (es solo un Y en los bits inferiores) y no afecta 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, en comparación SHRcon los turnos sin firmar). 2 de septiembre de 2010 a las 14:00
  • @Chris: Creo que quiere decir cuando el compilador debe permitir valores negativos donde "solo un Y en los bits inferiores" no se cumple; por ejemplo, -1% 8 da -1 en este sistema de complemento a dos usando g ++ (el signo en este caso es la implementación definida por 5.6 / 4).
    Roger Pate
    2 de septiembre de 2010 a las 15:45
  • 3
    @Chris: Estoy de acuerdo contigo en que R está exagerando el impacto; Solo vi tu comentario y supe que un simple Y no era suficiente.
    Roger Pate
    2 de septiembre de 2010 a las 23:21
  • 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 solucionarlo 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
    4 de marzo de 2011 a las 23:47
dieciséis

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.

2
  • 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 conmutador. El error se debe a que está intentando declarar una variable después de una declaración, lo cual es ilegal en C.
    MrZebra
    18/12/11 a las 15:26
  • 1
    Hoy en día es legal en c90 y la versión más reciente de c 19 de diciembre de 2011 a las 4:39
12

Mi truco favorito del interruptor maligno es usar un if (0) para omitir 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.

3
  • Muy agradable. Ejemplo de por qué: el caso 0 y el caso 1 podrían, por ejemplo, inicializar una variable de manera diferente que luego se usa en el caso 2.
    hlovdal
    19 mar 09 a las 10:02
  • 1
    Si desea que tanto el caso 0 como el caso 1 pasen por el caso 2. (sin que el caso 0 pase por el caso 1). No sé si es realmente útil, pero seguro que funciona.
    Petruza
    18 dic 2011 a las 6:55
  • 1
    Puede simplemente saltar a la etiqueta requerida gotosin ofuscar el código 24 sep 2016 a las 19:12
10

Prueba esto:

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

Puede declarar variables dentro de una declaración de cambio si comienza un nuevo bloque:

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

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

1
  • 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. 18 de septiembre de 2008 a las 14:12
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 se 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 las llaves).

No soy un 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 guardia de caso no lo es.

2
  • 3
    @ Mr.32: en realidad su ejemplo muestra que no se ejecuta un printf, pero en este caso, el int x no es una declaración sino una declaración, se declara la x, se reserva un espacio para ello cada vez que se apila el entorno de la función, ver: codepad.org/4E9Zuz1e
    Petruza
    18 dic '11 a las 7:03
  • 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 hablaron sobre las variables en las declaraciones de cambio.
    cesss
    3 de enero de 2018 a las 22:47
4

Toda la sección del conmutador es un contexto de declaración único. No puede declarar una variable en una declaración de caso como esa. Prueba esto en su lugar:

switch (val)  
{  
case VAL:
{
  // This will work
  int newVal = 42;
  break;
}
case ANOTHER_VAL:  
  ...
  break;
}
2
  • La variable se puede declarar, pero no se puede inicializar. 18 de septiembre de 2008 a las 14:11
  • @Richard Corden Estoy seguro de que la inicialización funcionará. ¿Sigues afirmando que no se puede inicializar? 13/0913 a las 19:07
3

Si su código dice "int newVal = 42", entonces razonablemente esperaría que newVal nunca se anule la inicialización. Pero si pasa por alto esta declaración (que es lo que está haciendo), entonces eso es exactamente lo que sucede: newVal está dentro del alcance pero no ha sido 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 a un solo caso, que es más probable que desee.

Puede aclarar las cosas si considera el mismo ejemplo pero con "const int newVal = 42;"

3

Solo quería enfatizar el punto de Slim . Una construcción de interruptor crea un alcance ciudadano completo de primera clase. Por lo tanto, es posible declarar (e inicializar) una variable en una declaración de cambio antes de la primera etiqueta de caso, sin un par de corchetes adicional:

switch (val) {  
  /* This *will* work, even in C89 */
  int newVal = 42;  
case VAL:
  newVal = 1984; 
  break;
case ANOTHER_VAL:  
  newVal = 2001;
  break;
}
2
  • -1 aquí int newVal = 42; nunca será ejecutado. ver este codepad.org/PA1quYX3 18/12/11 a las 5:55
  • 4
    int newVal se ejecutará la declaración , pero no la = 42asignación.
    Petruza
    18 dic 2011 a las 7:10
3

Hasta ahora, las respuestas han sido para C ++.

Para C ++, no puede saltar 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, C válido (pero feo), C ++ inválido

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();
}
1
  • 1
    El segundo ejemplo NO es válido para 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' 26/12/11 a las 9:50
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.

1
  • 1
    Primero no está bien en gcc 4.2: "error: expresión esperada antes de 'int'". Como dicen Peter y Mr.32, "caso 0:; int j; ..." y "caso 0:; int j = 7; ..." funcionan ambos. El problema en C es que "case <etiqueta>: declaración" no es una sintaxis C válida. 30 de mayo de 2012 a las 21:21
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 a alguien a quien le gusten las referencias al estándar le resulte ú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

labeled-statement:

...

  • attribute-specifier-seqopt case constant-expression : statement

...

Y en la C++ declaración, la declaración también se considera una declaración (en lugar de C):

N3337 1/6:

statement:

...

declaration-statement

...

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

Porque: N3337 6.7 / 3

It is possible to transfer into a block, but not in a way that bypasses declarations with initialization. A program that jumps (The transfer from the condition of a switch statement to a case label is considered a jump in this respect.)

from a point where a variable with automatic storage duration is not in scope to a point where it is in scope is ill-formed unless the variable has scalar type, class type with a trivial default constructor and a trivial destructor, a cv-qualified version of one of these types, or an array of one of the preceding types and is declared without an initializer (8.5).

Dado que kes de tipo escalar y no se inicializa en el punto de declaración, es posible saltar sobre su declaración. 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;
2

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

Considere esta switchdeclaración:

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

Puede resultar 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 ahora es fácil ver por qué no puede declarar dos variables con el mismo nombre dentro de un switchbloque.

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

1

Las nuevas variables solo se pueden clasificar 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

0

Creo que el problema en cuestión es que si se omitió la declaración e intentó usar la var en otro lugar, no se declararía.

0

newVal existe en todo el alcance del interruptor, pero solo se inicializa si se golpea la rama VAL. Si crea un bloque alrededor del código en VAL, debería estar bien.

0

El estándar C ++ 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 a un punto en el que está dentro del alcance está mal formado a menos que la variable tenga el 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;
}
0

Parece que los objetos anónimos se pueden declarar o crear en una declaración de caso de cambio por la razón de que no se puede hacer referencia a ellos y, como tales, no pueden pasar al siguiente caso. Considere este ejemplo compilado en GCC 4.5.3 y Visual Studio 2008 (aunque podría ser un problema de cumplimiento, por lo que los expertos deben opinar)

#include <cstdlib>

struct Foo{};

int main()
{
    int i = 42;

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

    default:
        break;
    }
    return EXIT_SUCCESS;
}
3
  • Si va a rechazarlo, explique por qué. Tengo curiosidad por saber por qué la creación de un objeto anónimo parece ser una excepción.
    Olumide
    31 de mayo de 2016 a las 14:03
  • 1
    no un DV, pero: Toda la pregunta es sobre la declaración / alcance de las variables nombradas. 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 esté). Por lo tanto, es completamente irrelevante. 17/07/2016 a las 17:55
  • Foo();no es una declaración; la pregunta es sobre declaraciones.
    M.M
    6 de julio de 2017 a las 4:02