tutorial programming online language book c language-design

programming - ¿Por qué la declaración del interruptor fue diseñada para necesitar un descanso?



c++ language (8)

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 semántica de salto automático para los bloques? ¿Cuál fue el razonamiento?


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

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


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

Dado que en el nivel de lenguaje ensamblador no importa si se divide entre cada uno o no, de todos modos hay una sobrecarga para todos los casos, entonces, ¿por qué no permitirlos ya que ofrecen ventajas significativas en ciertos casos?


En muchos sentidos, c es solo una interfaz limpia para las expresiones de ensamblaje estándar. Al escribir el control de flujo impulsado por la tabla de salto, el programador tiene la opción de caer o saltar de la "estructura de control", y un salto requiere una instrucción explícita.

Entonces, c hace lo mismo ...


Los enunciados de caso en una instrucción switch son simplemente etiquetas.

Cuando se enciende un valor, la instrucción switch esencialmente hace un goto a la etiqueta con el valor correspondiente.

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

En cuanto a la razón por la que se implementó de esta manera, la naturaleza de caída de una declaración de cambio 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;


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

Creo que fue simplemente un error, debido en gran parte a que, cuando se diseñó C, no había tanta experiencia en cómo se usarían estos constructos.

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

Analizamos las fuentes del compilador de Sun C para ver con qué frecuencia se utilizaba el método predeterminado. El frente del compilador ANSI C de Sun tiene 244 instrucciones de conmutación, cada una de las cuales tiene un promedio de siete casos. La caída ocurre en solo el 3% de todos estos casos.

En otras palabras, el comportamiento normal del interruptor es incorrecto el 97% del tiempo. No se trata solo de un compilador; al contrario, cuando se utilizó Fallout en este análisis, a menudo ocurría en situaciones que ocurren más frecuentemente en un compilador que en otro software, por ejemplo, al compilar operadores que pueden 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 de casos es tan ampliamente reconocida como un defecto que incluso hay una convención de comentarios especiales, que se muestra arriba, que dice que "esto es realmente uno de esos 3% de los casos en los que se desea fracasar".

Creo que fue una buena idea que C # requiera una instrucción de salto explícita 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 instrucciones). En C #, aún puede tener un caso que se le escape a otro: solo tiene que hacer que la caída sea explícita saltando al siguiente caso usando un goto .

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


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); } }


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 */ }

Piensa en la palabra clave case como una etiqueta goto y viene mucho más naturalmente.


Si los casos se diseñaron para romperse implícitamente, entonces no se podría tener Fallthrough.

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 interruptor fue diseñado para estallar implícitamente después de cada caso, no podría elegir. La estructura del conmutador se diseñó de la manera en que es más flexible.