¿Por qué la declaración de cambio fue diseñada para necesitar un descanso?

StackOverflow https://stackoverflow.com/questions/252489

  •  05-07-2019
  •  | 
  •  

Pregunta

Dada una simple instrucción de cambio

switch (int)
{
    case 1 :
    {
        printf("1\n");
        break;
    }

    case 2 : 
    {
        printf("2\n");
    }

    case 3 : 
    {
        printf("3\n");
    }
}

La ausencia de una declaración de interrupción en el caso 2 implica que la ejecución continuará dentro del código para el caso 3. Esto no es un accidente; Fue diseñado de esa manera. ¿Por qué se tomaron estas decisiones? ¿Qué beneficio proporciona esto frente a tener una ruptura semántica automática para los bloques? ¿Cuál fue la razón?

¿Fue útil?

Solución

Muchas respuestas parecen centrarse en la capacidad de fallar como la razón para requerir la declaración break .

Creo que fue simplemente un error, debido en gran medida a que cuando se diseñó C no había tanta experiencia sobre cómo se usarían estas construcciones.

Peter Van der Linden expone el caso en su libro "Expert C Programming":

  

Analizamos las fuentes del compilador Sun C   para ver con qué frecuencia caen por defecto   a través de fue utilizado. El sol ANSI C   compilador frontal tiene 244 interruptor   declaraciones, cada una de las cuales tiene una   Promedio de siete casos. Caer a través   ocurre en solo el 3% de todos estos casos.

     

En otras palabras, el cambio normal   el comportamiento es incorrecto el 97% del tiempo.   No es solo en un compilador, en el   Por el contrario, donde se utilizó la caída   en este análisis fue a menudo para   situaciones que ocurren con más frecuencia   en un compilador que en otro software,   por ejemplo, al compilar operadores   que puede tener uno o dos   operandos:

switch (operator->num_of_operands) {
    case 2: process_operand( operator->operand_2);
              /* FALLTHRU */

    case 1: process_operand( operator->operand_1);
    break;
}
     

La caída del caso es tan amplia   reconocido como un defecto que hay   incluso una convención especial de comentarios,   se muestra arriba, eso le dice a pelusa " esto es   Realmente uno de esos 3% de casos donde   se deseaba caer. "

Creo que fue una buena idea que C # requiera una instrucción explícita de salto al final de cada bloque de casos (al tiempo que permite que se apilen varias etiquetas de casos, siempre que haya un solo bloque de declaraciones). En C # todavía puede hacer que un caso se caiga a otro: solo tiene que hacer el paso a través de un modo explícito saltando al siguiente caso utilizando un goto .

Es una lástima que Java no aprovechó la oportunidad para romper con la semántica de C.

Otros consejos

De muchas maneras, c es solo una interfaz limpia para los modismos de ensamblaje estándar. Al escribir el control de flujo accionado por la tabla de salto, el programador tiene la opción de salirse o saltar fuera de la "estructura de control", y un salto requiere una instrucción explícita.

Entonces, c hace lo mismo ...

Para implementar el dispositivo de Duff, obviamente:

dsend(to, from, count)
char *to, *from;
int 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);
    }
}

Si los casos se diseñaron para romperse de manera implícita, entonces no podría haber fracasado.

case 0:
case 1:
case 2:
    // all do the same thing.
    break;
case 3:
case 4:
    // do something different.
    break;
default:
    // something else entirely.

Si el conmutador se diseñó para romperse implícitamente después de cada caso, no tendría elección. La estructura de la caja de conmutación se diseñó de la manera que sea más flexible.

Las declaraciones de caso en las declaraciones de cambio son simplemente etiquetas.

Cuando enciendes un valor, la declaración de cambio esencialmente hace un goto a la etiqueta con el valor correspondiente.

Esto significa que la interrupción es necesaria para evitar pasar al código debajo de la siguiente etiqueta.

En cuanto a la razón por qué se implementó de esta manera: la naturaleza directa de una declaración de conmutación puede ser útil en algunos escenarios. Por ejemplo:

case optionA:
    // optionA needs to do its own thing, and also B's thing.
    // Fall-through to optionB afterwards.
    // Its behaviour is a superset of B's.
case optionB:
    // optionB needs to do its own thing
    // Its behaviour is a subset of A's.
    break;
case optionC:
    // optionC is quite independent so it does its own thing.
    break;

Para permitir cosas como:

switch(foo) {
case 1:
    /* stuff for case 1 only */
    if (0) {
case 2:
    /* stuff for case 2 only */
    }
    /* stuff for cases 1 and 2 */
case 3:
    /* stuff for cases 1, 2, and 3 */
}

Piense en la palabra clave case como una etiqueta goto y es mucho más natural.

Elimina la duplicación de código cuando varios casos necesitan ejecutar el mismo código (o el mismo código en secuencia).

Ya que en el nivel de lenguaje ensamblador, no importa si se rompe entre cada uno o no, no hay sobrecarga para la caída de los casos de todos modos, así que ¿por qué no permitirlos, ya que ofrecen ventajas significativas en ciertos casos?

Me topé con un caso de asignación de valores en vectores a estructuras: tenía que hacerse de tal manera que si el vector de datos fuera más corto que el número de miembros de datos en la estructura, el resto de los miembros permanecería en su valor predeterminado. En ese caso, omitir break fue bastante útil.

switch (nShorts)
{
case 4: frame.leadV1    = shortArray[3];
case 3: frame.leadIII   = shortArray[2];
case 2: frame.leadII    = shortArray[1];
case 1: frame.leadI     = shortArray[0]; break;
default: TS_ASSERT(false);
}

Como muchos han especificado aquí, es para permitir que un solo bloque de código funcione en varios casos. Esto debería ser una ocurrencia más común para sus declaraciones de cambio que el "bloque de código por caso". especifique en su ejemplo.

Si tiene un bloque de código por caso sin interrupción, quizás debería considerar usar un bloque if-elseif-else, ya que eso parecería más apropiado.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top