Considere la siguiente switch
declaración:
switch( value )
{
case 1:
return 1;
default:
value++;
// fall-through
case 2:
return value * 2;
}
Este código se compila, pero ¿es válido (= comportamiento definido) para C90 / C99? Nunca he visto código donde el caso predeterminado no sea el último.
EDITAR:
como Jon Cage escriben y KillianDS : este es un código realmente feo y confuso y lo sé muy bien. Solo estoy interesado en la sintaxis general (¿está definida?) Y el resultado esperado.
c
switch-statement
tanascio
fuente
fuente
goto
no es malo. Los seguidores del culto de carga son! No se puede imaginar hasta qué extremo puede ir la gente para evitarlogoto
porque es tan malvado, lo que hace que su código sea un verdadero lío ilegible.goto
principalmente para simular algo así como unafinally
cláusula en funciones, donde los recursos (archivos, memoria) deben liberarse al detenerse, y repetir para cada caso de error una lista defree
yclose
no ayuda para la legibilidad. Sin embargo, hay un uso degoto
eso que me gustaría evitar pero que no puedo, es cuando quiero salir de un bucle y estoy dentroswitch
de ese bucle.Respuestas:
El estándar C99 no es explícito sobre esto, pero tomando todos los hechos juntos, es perfectamente válido.
UNA
case
ydefault
etiqueta son equivalentes a unagoto
etiqueta. Ver 6.8.1 Declaraciones etiquetadas. Especialmente interesante es 6.8.1.4, que permite el dispositivo de Duff ya mencionado:Editar : el código dentro de un interruptor no es nada especial; es un bloque de código normal como en una
if
declaración, con etiquetas de salto adicionales. Esto explica el comportamiento de caída y por québreak
es necesario.6.8.4.2.7 incluso da un ejemplo:
Las constantes de mayúsculas y minúsculas deben ser únicas dentro de una declaración de cambio:
Todos los casos se evalúan, luego salta a la etiqueta predeterminada, si se proporciona:
fuente
default
caso que domina otros casos en aproximadamente 100: 1, y no sé si es válido o no está definido para hacerdefault
el primer caso.Las declaraciones de caso y la declaración predeterminada pueden aparecer en cualquier orden en la declaración de cambio. La cláusula predeterminada es una cláusula opcional que coincide si ninguna de las constantes en las declaraciones de caso puede coincidir.
Buen ejemplo :-
muy útil si desea que sus casos se presenten en un orden lógico en el código (como en, sin decir caso 1, caso 3, caso 2 / predeterminado) y sus casos son muy largos, por lo que no desea repetir todo el caso código en la parte inferior para el valor predeterminado
fuente
Es válido y muy útil en algunos casos.
Considere el siguiente código:
El punto es que el código anterior es más legible y eficiente que en cascada
if
. Podría ponerdefault
al final, pero no tiene sentido, ya que centrará su atención en los casos de error en lugar de los casos normales (que aquí es eldefault
caso).En realidad, no es un buen ejemplo,
poll
ya sabes cuántos eventos pueden ocurrir como máximo. Mi punto real es que no son casos con un conjunto definido de valores de entrada, donde hay 'excepciones' y los casos normales. Si es mejor poner excepciones o casos normales al frente es una cuestión de elección.En el campo del software, pienso en otro caso muy habitual: recursiones con algunos valores terminales. Si puedes expresarlo usando un interruptor,
default
será el valor habitual que contiene la llamada recursiva y elementos distinguidos (casos individuales) los valores del terminal. Por lo general, no es necesario centrarse en los valores terminales.Otra razón es que el orden de los casos puede cambiar el comportamiento del código compilado, y eso es importante para las actuaciones. La mayoría de los compiladores generarán código de ensamblaje compilado en el mismo orden en que aparece el código en el conmutador. Eso hace que el primer caso sea muy diferente de los demás: todos los casos, excepto el primero, implicarán un salto y eso vaciará las tuberías del procesador. Puede entenderlo como un predictor de rama que por defecto ejecuta el primer caso que aparece en el conmutador. Si un caso es mucho más común que los otros, entonces tiene muy buenas razones para ponerlo como el primer caso.
La lectura de comentarios es la razón específica por la cual el póster original hizo esa pregunta después de leer la reorganización del compilador de Intel Branch Loop sobre la optimización del código.
Entonces se convertirá en un arbitraje entre la legibilidad del código y el rendimiento del código. Probablemente sea mejor hacer un comentario para explicar al futuro lector por qué aparece primero un caso.
fuente
case
. Lo que es deprimente es que se ve igual que el azúcar sintáctico y no romperá ningún código existente si es compatible.Sí, esto es válido y, en algunas circunstancias, incluso es útil. En general, si no lo necesita, no lo haga.
fuente
No hay un orden definido en una declaración de cambio. Puede ver los casos como algo así como una etiqueta con nombre, como una
goto
etiqueta. Contrariamente a lo que la gente parece pensar aquí, en el caso del valor 2 no se salta a la etiqueta predeterminada. Para ilustrar con un ejemplo clásico, aquí está el dispositivo de Duff , que es el cartel hijo de los extremos deswitch/case
C.fuente
Un escenario en el que consideraría apropiado tener un 'valor predeterminado' ubicado en otro lugar que no sea el final de una declaración de caso es en una máquina de estado donde un estado no válido debería restablecer la máquina y proceder como si fuera el estado inicial. Por ejemplo:
Una disposición alternativa, si un estado no válido no debe restablecer la máquina, pero debe ser fácilmente identificable como un estado no válido:
El código en otro lugar puede buscar (widget_state == WIDGET_INVALID_STATE) y proporcionar cualquier comportamiento de informe de error o restablecimiento de estado que parezca apropiado. Por ejemplo, el código de barra de estado podría mostrar un icono de error, y la opción de menú "widget de inicio" que está deshabilitada en la mayoría de los estados no inactivos podría habilitarse para WIDGET_INVALID_STATE así como WIDGET_IDLE.
fuente
Intercambiando con otro ejemplo: Esto puede ser útil si "predeterminado" es un caso inesperado, y desea registrar el error pero también hacer algo sensato. Ejemplo de algunos de mi propio código:
fuente
Hay casos en los que está convirtiendo ENUM en una cadena o convirtiendo cadena en enum en caso de que esté escribiendo / leyendo a / desde un archivo.
En ocasiones, debe establecer uno de los valores predeterminados para cubrir los errores cometidos al editar archivos manualmente.
fuente
La
default
condición puede ser en cualquier lugar dentro del conmutador que puede existir una cláusula de caso. No es obligatorio ser la última cláusula. He visto código que pone el valor predeterminado como la primera cláusula. loscase 2:
se ejecuta normalmente, aunque la cláusula predeterminada está por encima de él.Como prueba, puse el código de muestra en una función, llamé
test(int value){}
y ejecuté:El resultado es:
fuente
Es válido, pero bastante desagradable. Sugeriría que en general es malo permitir fallos, ya que puede conducir a un código de espagueti muy desordenado.
Es casi seguro que sea mejor dividir estos casos en varias declaraciones de cambio o funciones más pequeñas.
[edit] @Tristopia: Tu ejemplo:
sería más claro en cuanto a su intención (creo) si se escribiera así:
[edit2] @Tristopia: Su segundo ejemplo es probablemente el ejemplo más claro de un buen uso para el seguimiento:
... pero personalmente dividiría el reconocimiento de comentarios en su propia función:
fuente
r
es la matriz de destino,wc
es elwchar_t
conmutador de entrada (utf8_length) {/ * Nota: ¡el código cae por los casos! * / caso 3: r [2] = 0x80 | (wc y 0x3f); wc >> = 6; wc | = 0x800; caso 2: r [1] = 0x80 | (wc y 0x3f); wc >> = 6; wc | = 0xc0; caso 1: r [0] = wc; }for(i=0; s[i]; i++) { switch(s[i]) { case '"': case '\'': case '\\': d[dlen++] = '\\'; /* fall through */ default: d[dlen++] = s[i]; } }