¿Sintaxis válida, pero sin valor en switch-case?


A través de un pequeño error, accidentalmente encontré esta construcción:

int main(void) {
    char foo = 'c';

    switch(foo)
    {
        printf("Cant Touch This\n");   // This line is Unreachable

        case 'a': printf("A\n"); break;
        case 'b': printf("B\n"); break;
        case 'c': printf("C\n"); break;
        case 'd': printf("D\n"); break;
    }

    return 0;
}

Parece que la printf en la parte superior de la declaración switch es válida, pero también completamente inalcanzable.

Tengo una compilación limpia, sin siquiera una advertencia sobre el código inalcanzable, pero esto parece inútil.

¿Debería un compilador marcar esto como código inalcanzable?
¿Esto tiene algún propósito?

Author: abelenky, 2017-01-18

8 answers

Quizás no sea el más útil, pero no completamente inútil. Puede usarlo para declarar una variable local disponible dentro del ámbito switch.

switch (foo)
{
    int i;
case 0:
    i = 0;
    //....
case 1:
    i = 1;
    //....
}

El estándar (N1579 6.8.4.2/7) tiene la siguiente muestra:

EJEMPLO En el fragmento de programa artificial

switch (expr)
{
    int i = 4;
    f(i);
case 0:
    i = 17;
    /* falls through into default code */
default:
    printf("%d\n", i);
}

El objeto cuyo identificador es i existe con duración de almacenamiento automático (dentro del bloque) pero nunca inicializado, y por lo tanto si la expresión controladora tiene un valor distinto de cero, llamar a la función printf accede a un valor indeterminado. Del mismo modo, la llamada a la función f no se puede alcanzar.

P.d. POR cierto, el ejemplo no es código C++ válido. En ese caso (N4140 6.7/3, énfasis mío):

Un programa que salta90 desde un punto donde una variable con duración de almacenamiento automático no está en el alcance a un punto donde está en el alcance está mal formado a menos que la variable tenga tipo escalar, tipo de clase con un valor predeterminado trivial constructor y un destructor trivial, una versión calificada cv de uno de estos tipos, o una matriz de uno de los tipos precedentes y se declara sin inicializador (8.5).


90) La transferencia de la condición de una instrucción switch a una etiqueta case se considera un salto en este sentido.

Así que reemplazar int i = 4; por int i; lo convierte en un C++válido.

 220
Author: AlexD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-19 21:45:58

¿Esto tiene algún propósito?

Sí. Si en lugar de una declaración, pones una declaración antes de la primera etiqueta, esto puede tener perfecto sentido:

switch (a) {
  int i;
case 0:
  i = f(); g(); h(i);
  break;
case 1:
  i = g(); f(); h(i);
  break;
}

Las reglas para declaraciones y declaraciones se comparten para bloques en general, por lo que es la misma regla que permite que eso también permite declaraciones allí.


Vale la pena mencionar también que si la primera instrucción es una construcción de bucle, las etiquetas de mayúsculas y minúsculas pueden aparecer en el bucle cuerpo:

switch (i) {
  for (;;) {
    f();
  case 1:
    g();
  case 2:
    if (h()) break;
  }
}

Por favor, no escriba código como este si hay una forma más legible de escribirlo, pero es perfectamente válido, y la llamada f() es accesible.

 98
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-18 19:14:54

Hay un uso famoso de esto llamado El Dispositivo de Duff.

int n = (count+3)/4;
switch (count % 4) {
  do {
    case 0: *to = *from++;
    case 3: *to = *from++;
    case 2: *to = *from++;
    case 1: *to = *from++;
  } while (--n > 0);
}

Aquí copiamos un búfer apuntado por from a un búfer apuntado por to. Copiamos count instancias de datos.

La instrucción do{}while() comienza antes de la primera etiqueta case, y las etiquetas case están incrustadas dentro de la do{}while().

Esto reduce el número de ramas condicionales al final del bucle do{}while() encontrado por aproximadamente un factor de 4 (en este ejemplo; la constante se puede ajustar a cualquier valor que quieras).

Ahora, los optimizadores a veces pueden hacer esto por usted (especialmente si están optimizando instrucciones de transmisión/vectorizadas), pero sin la optimización guiada por perfil no pueden saber si espera que el bucle sea grande o no.

En general, las declaraciones de variables pueden ocurrir allí y ser utilizadas en todos los casos, pero estar fuera de alcance después de que finalice el interruptor. (tenga en cuenta que cualquier inicialización se omitirá)

Además, controle el flujo que no es específico del interruptor puede entrar en esa sección del bloque switch, como se ilustra arriba, o con un goto.

 38
Author: Yakk - Adam Nevraumont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-19 15:15:11

Suponiendo que esté utilizando gcc en Linux, le habría dado una advertencia si está utilizando la versión 4.4 o anterior.

La opción-Wunreachable-code se eliminó en gcc 4.4 en adelante.

 15
Author: 16tons,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-14 19:31:00

No solo para la declaración de variables, sino también para el salto avanzado. Se puede utilizar bien si y solo si usted no es propenso al código espagueti.

int main()
{
    int i = 1;
    switch(i)
    {
        nocase:
        printf("no case\n");

        case 0: printf("0\n"); break;
        case 1: printf("1\n"); goto nocase;
    }
    return 0;
}

Imprime

1
no case
0 /* Notice how "0" prints even though i = 1 */

Debe tenerse en cuenta que switch-case es una de las cláusulas de flujo de control más rápidas. Así que debe ser muy flexible para el programador, que a veces implica casos como este.

 11
Author: Sanchke Dellowar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-18 19:26:33

Debe tenerse en cuenta que prácticamente no hay restricciones estructurales en el código dentro de la instrucción switch, o en dónde se colocan las etiquetas case *: dentro de este código*. Esto hace posibles trucos de programación como el dispositivo de duff, una posible implementación de la cual se ve así:

int n = ...;
int iterations = n/8;
switch(n%8) {
    while(iterations--) {
        sum += *ptr++;
        case 7: sum += *ptr++;
        case 6: sum += *ptr++;
        case 5: sum += *ptr++;
        case 4: sum += *ptr++;
        case 3: sum += *ptr++;
        case 2: sum += *ptr++;
        case 1: sum += *ptr++;
        case 0: ;
    }
}

Verás, el código entre la etiqueta switch(n%8) { y la etiqueta case 7: es definitivamente accesible...


* Como supercat afortunadamente señaló en un comentario: Desde C99, ni una goto ni una etiqueta (ya sea una etiqueta case *: o no) pueden aparecer en el ámbito de una declaración que contenga una declaración VLA. Así que no es correcto decir que no hay restricciones estructurales en la colocación de las etiquetas case *:. Sin embargo, el dispositivo de duff es anterior al estándar C99, y no depende del VLA de todos modos. Sin embargo, me sentí obligado a insertar un "virtualmente" en mi primera oración debido a esto.

 11
Author: cmaster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:27

Tienes tu respuesta relacionada con la opción requerida gcc -Wswitch-unreachable para generar la advertencia, esta respuesta es elaborar sobre la usabilidad / worthyness part.

Citando directamente de C11, capítulo §6.8.4.2, (énfasis mío)

switch (expr)
{
int i = 4;
f(i);
case 0:
i = 17;
/* falls through into default code */
default:
printf("%d\n", i);
}

El objeto cuyo identificador es i existe con almacenamiento automático duration (within the block) but is never initialized, and thus if the la expresión de control tiene un valor distinto de cero, la llamada a la printf la función accederá a un valor indeterminado. Del mismo modo, el llamado a no se puede acceder a la función f.

Que es muy autoexplicativo. Puede usar esto para definir una variable de ámbito local que solo está disponible dentro del ámbito de la instrucción switch.

 10
Author: Sourav Ghosh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-18 19:13:29

Es posible implementar un "bucle y medio" con él, aunque podría no ser la mejor manera de hacerlo:

char password[100];
switch(0) do
{
  printf("Invalid password, try again.\n");
default:
  read_password(password, sizeof(password));
} while (!is_valid_password(password));
 9
Author: celtschk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-19 09:58:38