2010-07-29 18 views
6

estoy interesado si hay alguna diferencia con respecto al C o C++ perspectiva compilador si utilizo:¿Hay alguna diferencia en C y C++ entre usar if, else if, else if, ... y usar el interruptor() {caso A: ... caso B: ...}?

if (value == a) { 
    ... 
} 
else if (value == b) { 
    ... 
} 
else if (value == c) { 
    ... 
} 

frente

switch (value) { 
    case a: 
     ... 
     break; 
    case b: 
     ... 
     break; 
    case c: 
     ... 
     break; 
} 

Se siente a mí que no hay ninguna diferencia, simplemente sintáctica. ¿Alguien sabe más al respecto?

Gracias, Boda Cydo.

Respuesta

6

Hay una diferencia: con los conmutadores, el compilador puede optimizar el conmutador para usar una tabla de búsqueda. Esto puede ser posible si hay muchos valores que están lo suficientemente cerca el uno del otro. Por ejemplo, este interruptor:

switch (integer) { 
    case 10: 
    xxx 
    break; 
    case 12: 
    yyy 
    break; 
    case 13 
    zzz 
    break; 
} 

podría convertirse (pseudocódigo):

address = lookup[ integer - 10 ]; // which is prefilled with { case_10, err, err, case_12, case 13 } 
goto address; 
case_10: xxx; goto err; 
case_12: yyy; goto err; 
case_13: zzz; 
err: //do nothing 
+0

Con 'if's, bajo ciertas circunstancias, el compilador puede optimizar el árbol' if' para usar una tabla de búsqueda. Esto es mucho menos probable, con los compiladores actuales, pero no va más allá de lo que dice el Estándar. –

0

Un conmutador se debe compilar a un salto con direccionamiento indirecto, mientras que una secuencia de enunciados if será una cadena de saltos condicionales. El primero es el tiempo constante; por supuesto, puedes poner condiciones mucho más generales en un if.

Editar: Debo mencionar que no me sorprendería si algunos compiladores inteligentes son capaces de detectar que todas las condiciones en una cadena de ifs tienen una forma particular simple y se convierten en un interruptor. No sé si lo hacen, pero siempre puedes descompilar y verificar.

8

Sí, hay diferencias. La evaluación en cascada de if s garantiza las condiciones en orden. El interruptor garantiza solo una evaluación de lo que se usa como parámetro de cambio. Dependiendo del compilador, el interruptor a menudo tomará (casi) tiempo constante independientemente de la rama seleccionada, mientras que la cascada if prácticamente garantiza que el primer tramo sea el más rápido, el segundo segundo más rápido, y así sucesivamente hasta el último sea el más lento .

+2

No es así de simple. Si la condición no tiene efectos secundarios (como en el OP), el compilador puede reordenar un if/else con bastante facilidad. Por ejemplo, [gcc] (http://gcc.gnu.org/onlinedocs/gcc-4.5.0/gcc/Optimize-Options.html#Optimize-Options) puede hacer esto con opciones como '-freorder-blocks' y '-fguess-branch-probability'. El compilador también puede generar una tabla de salto desde una cascada de if en ciertas circunstancias. Finalmente, las sentencias 'switch' no darán como resultado una tabla de salto si los valores son demasiado escasos. –

1

Esto dependerá de cómo el compilador elija optimizar su código. La optimización de código para un compilador es un campo enorme.

Para encontrar la respuesta exacta para su compilador, determine cómo compilar el código ensamblador con él y observe el código de ensamblaje diferente que se escribe en el archivo.

Esto ya se hizo con un compilador y puede ver los resultados aquí. http://www.eventhelix.com/RealtimeMantra/Basics/CToAssemblyTranslation3.htm

Pero las respuestas cortas cortas son sí. lo más probable es que sean diferentes.

0

La declaración de caso puede obtener compilado a un "salto mesa", que puede ser más rápido si hay decenas de casos y ejecutar esto millones de veces.

0

Es posible, si el valor de selección es un número entero (que debe estar en C/C++), que el compilador podría reemplazar el if con una tabla de salto.

3

Existen varias diferencias, según el estándar.

  1. El value puede evaluarse varias veces en la cadena de if, una vez en el estado de switch. Si la evaluación de value no tiene efectos secundarios, esto no es importante.
  2. La cadena if no permitirá la caída, mientras que la instrucción switch tendrá caída sin el break.
  3. La cadena if permite la comparación general, pero la instrucción switch solo permite comparaciones con expresiones integrales constantes.
  4. El uso de break; es diferente. Se rompe de la declaración switch, pero más allá. Si necesita salir de un bucle o adjuntar la declaración switch según una condición, necesita la cadena if.
  5. Desde hace una declaración switch esencialmente un goto a una declaración o default:case, va a trabajar en diferentes lugares, siendo el ejemplo por excelencia Duff's device. (IIRC, Tom Duff consideró que era un fuerte argumento en la cuestión de fallthrough, pero que no estaba seguro de qué lado.)

lo tanto, si se evalúa value sin efectos secundarios, break; declaraciones se usan de manera habitual y sólo en el switch, la comparación es a valores integrales constantes, y no se usa de manera funky, el comportamiento puede ser idéntico. Si cualquier compilador usará esta equivalencia es otra pregunta.

0

+1 para la respuesta de David Thomley, ya que realmente encuentro esta la más completa.

Falta una cosa importante, es decir que las etiquetas case deben ser expresiones constantes que se evalúan en tiempo de compilación. Con if ambos lados de la comparación (si reduce la declaración if a eso) se evalúan en tiempo de ejecución.

1

Vine al mismo problema así que hice algunas pruebas, aquí están algunos de los resultados obtenidos utilizando la versión de gcc 3.4.6/centos 4

CA y utilizará cc IFS, pero cc toma la variable de línea de comandos para el compilador no conoce el valor de "b" en el momento de la compilación. b.c utiliza cambiar

códigos fuente:

A.C.

#include <stdint.h> 
int main(){ 
uint32_t i,b=10,c; 
    for(i=0;i<1000000000;i++){ 
     if(b==1) c=1; 
     if(b==2) c=1; 
     if(b==3) c=1; 
     if(b==4) c=1; 
     if(b==5) c=1; 
     if(b==6) c=1; 
     if(b==7) c=1; 
    } 
} 

b.c

#include <stdint.h> 
int main(){ 
uint32_t i,b=10,c; 
    for(i=0;i<1000000000;i++){ 
     switch(b){ 
     case 1: 
       c=1; 
       break; 
     case 2: 
       c=1; 
       break; 
     case 3: 
       c=1; 
       break; 
     case 4: 
       c=1; 
       break; 
     case 5: 
       c=1; 
       break; 
     case 6: 
       c=1; 
       break; 
     case 7: 
       c=1; 
       break; 
     } 
    } 
} 

C.C

#include <stdint.h> 
int main(int argc, char **argv){ 
uint32_t i,b=10,c; 

    b=atoi(argv[1]); 
    for(i=0;i<1000000000;i++){ 
     if(b==1) c=1; 
     if(b==2) c=1; 
     if(b==3) c=1; 
     if(b==4) c=1; 
     if(b==5) c=1; 
     if(b==6) c=1; 
     if(b==7) c=1; 
    } 
} 

primera compilamos los programas sin parámetros de optimización:

[email protected] ~ # gcc a.c -o a;gcc b.c -o b;gcc c.c -o c 
[email protected] ~ # time ./a 

real 0m4.871s 
user 0m4.866s 
sys  0m0.005s 
[email protected] ~ # time ./b 

real 0m1.904s 
user 0m1.904s 
sys  0m0.000s 
[email protected] ~ # time ./c 10 

real 0m4.848s 
user 0m4.836s 
sys  0m0.009s 

Los resultados son como pensaba, el cambio es más rápido que si no se utiliza la optimización del compilador.

Ahora compilamos usando O2:

[email protected] ~ # gcc a.c -o a -O2;gcc b.c -o b -O2;gcc c.c -o c -O2 
[email protected] ~ # time ./a 

real 0m0.055s 
user 0m0.055s 
sys  0m0.000s 
[email protected] ~ # time ./b 

real 0m0.537s 
user 0m0.535s 
sys  0m0.001s 
[email protected] ~ # time ./c 10 

real 0m0.056s 
user 0m0.055s 
sys  0m0.000s 

Sorpresa sorpresa, ambos programas (A.C. y C.C) utilizando IFS son más rápidos que el interruptor (alrededor de 10 veces!).