ESTRUCTURAS DE CONTROL - SENTENCIA SWITCH







Sentencia switch .
En las secciones anteriores se ha estudiado un tipo de flujo de control limitado a las posibilidades ofrecidas por las sentencias alternativas ( if-else ) y repetititivas ( for , while ). Las sentencias alternativas permiten especificar la reacción del programa en distintas circunstancias; si la lógica del algoritmo empleado se ajusta a un comportamiento más o menos dicotómico, es posible construir un árbol de sentencias if() anidadas que generen el comportamiento necesario. Sin embargo, es frecuente hallar grupos de situaciones (y por tanto de respuestas de programa) que se reducen a una lista de posibilidades mutuamente excluyentes. Ciertamente, se puede implementar un algoritmo de este tipo mediante múltiples sentencias if()-else anidadas, pero esta solución resulta farragosa y poco eficiente, porque el código tiene que ir comprobando una por una todas las posibilidades (todas las condiciones if() ) hasta encontrar la correcta.
  El lenguaje C ofrece otra posibilidad: construir una expresión ordinal (posiblemente basada en un tipo enumerado) que tipifique las posibles situaciones y ejecutar el código correspondiente a cada valor de esta expresión sin necesidad de examinar los demás. La sentencia switch es un selector multivía, que admite como argumento el valor de una expresión ordinal, y desencadena la ejecución del código correspondiente, identificado mediante uno o más valores constantes del mismo tipo ordinal que la expresión. La sintaxis de esta sentencia es como sigue:
switch( expresión_ordinal )
{
  case constante_1  : Bloque_1;
              break;
  case constante_2  : Bloque_2;
              break;
  case constante_3  :
  case constante_4  :
  case constante_5  : Bloque_3;
              break;
  ...
  default : Bloque_4;
          break;
}

Al evaluar la expresión_ordinal se obtiene un entero (quizá el valor de una variable de tipo enum ) o un carácter. Si este valor es igual a la constante que acompaña a alguna de las cláusulas case presentes en el cuerpo de la sentencia switch , entonces se ejecuta el bloque de código situado a la derecha de los dos puntos que acompañan a la cláusula case en cuestión. El bloque puede estar formado por una o más sentencias. Puede haber varias cláusulas case asociadas a un mismo bloque de código; en tal caso, el bloque ejecutado será el mismo para todos aquellos casos en que el valor de la expresión de control sea el de cualquiera de las cláusulas case asociadas a ese bloque. El programa siguiente es un ejemplo sencillo de uso de la sentencia switch :

#include<stdio.h>

int main(int contador, char * argumento[])
{
 int i;
 switch(contador)
  {
   case 1: printf("\nNo me ha dado argumentos.\n");
       break;
   case 2: printf("\nMe ha dado un argumento.\n");
       break;
   case 3:
   case 4:
   case 5: printf("\nMe ha dado 2, 3 o 4 argumentos.\n");
       break;
   default: printf("\nMe ha dado más de 4 argumentos.\n");
        break;
  }
 printf("\n");
 /*
  Ahora se muestran los argumentos dados, si los hay
 */
 for(i=0;i<contador;i++)
  printf("Argumento[%d] = %s\n", i, argumento[i]);

 printf("\nTerminación normal del programa\n\n");
 return 0;
}



El resultado de ejecutar este programa es como sigue:

[cauldron:~/coti>]> ./a.out

No me ha dado argumentos.

Argumento[0] = ./a.out

Terminación normal del programa

[cauldron:~/coti>]> ./a.out 1

Me ha dado un argumento.

Argumento[0] = ./a.out
Argumento[1] = 1

Terminación normal del programa

[cauldron:~/coti>]> ./a.out 1 2 3

Me ha dado 2, 3 o 4 argumentos.

Argumento[0] = ./a.out
Argumento[1] = 1
Argumento[2] = 2
Argumento[3] = 3

Terminación normal del programa

[cauldron:~/coti>]> ./a.out 1 2 3 4 5 6 7

Me ha dado más de 4 argumentos.

Argumento[0] = ./a.out
Argumento[1] = 1
Argumento[2] = 2
Argumento[3] = 3
Argumento[4] = 4
Argumento[5] = 5
Argumento[6] = 6
Argumento[7] = 7

Terminación normal del programa

[cauldron:~/coti>]>

Comentarios .- La variable contador recibe automáticamente como valor el número de argumentos que acompañan al nombre del programa en la línea de órdenes. Si no se dan argumentos, esto es, si lo único que se escribe es el nombre del programa, entonces el valor de contador es 1. Si se dan argumentos, se incrementa contador en tantas unidades como palabras distintas aparezcan en la línea de órdenes. Hemos utilizado contador como expresión (trivial, al ser una variable) de control en este ejemplo de sentencia switch .
El programa imprime en pantalla distintos comentarios en función del número de argumentos; se han considerado los casos en que:



Importancia de break
Cuando el control del programa llega a una sentencia switch y se evalúa la expresión de control de la misma, el programa comienza a ejecutar las sentencias presentes en el bloque correspondiente a alguna cláusula case, o quizá a la claúsula default. ¿Qué sucede después de ejecutar la última sentencia del bloque? Si el bloque es el último, el programa ejecuta la sentencia inmediatamente siguiente al switch. Pero si no es el último, el programa ejecuta la primera sentencia inmediatamente siguiente al bloque. Y luego la siguiente, y la siguente, etc. La sentencia break , sirve para hacer que el programa abandone la sentencia switch . Si no se encuentra un break , se ejecuta el siguiente bloque de código, y así hasta hallar un break o salir de la sentencia switch .
Dicho de otro modo, las cláusulas case son puntos de entrada al conjunto de líneas de código que hay en el interior del switch . Las sentencias break son puntos de salida, y debemos organizar cuidadosamente ambas cosas para evitar errores que no podrá detectar el compilador.
La estructura switch se utiliza con mucha frecuencia; véase el Ejercicio 1 .

Importancia de default .
La sentencia switch está especialmente indicada para construir menús: en efecto, se ofrece al usuario una colección de opciones, y este indica su preferencia pulsando una tecla o haciendo clic con el ratón en algún lugar. Esta elección del usuario es fácilmente transformable en números enteros, y una sentencia switch permite fácilmente asignar acciones a esos números. De hecho, la sentencia switch se va a utilizar para construir algunas esqueletos de programa, aun cuando prescindiremos de ella en cuanto dispongamos del interesante concepto de puntero de función .

Ejercicio 1 .- Construir un programa que resuelva ecuaciones de segundo grado empleando sentencias switch .
/*
  Este programa sirve para resolver ecuaciones cuadráticas.
  Es un ejemplo de utilización de la sentencia switch.
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

enum tipos_de_raices {reales_y_distintas, complejas_conjugadas,
              raiz_real_doble};
enum tipos_de_raices tipo;
float a, b, c, d;
float r1, r2, preal, pimaginaria, rrd;

int main(void)
{
 printf ("Ecuaciones cuadráticas");
 printf("\n\nEscriba los coeficientes a, b y c : ");
 scanf("%f %f %f", &a, &b, &c);

 d = b*b - 4.0*a*c;

 d > 0.0 ? tipo=reales_y_distintas :
  (d < 0.0 ? tipo=complejas_conjugadas : (tipo= raiz_real_doble));
 switch(tipo) {
 case reales_y_distintas :
     {
      r1=(-b+sqrt(d))/(2.0*a);
      r2=(-b-sqrt(d))/(2.0*a);
      printf("\nRaíces reales y distintas; r1= %f y r2= %f",
       r1, r2);
     }
     break;
 case complejas_conjugadas :
     {
      preal=(-b)/(2.0*a);
      pimaginaria=sqrt(-d)/(2.0*a);
      printf( "\nRaíces complejas conjugadas, r1= %f+j%f y r2= %f-j%f",
        preal, pimaginaria, preal, pimaginaria);
     }
     break;
 case raiz_real_doble:
     {
      rrd=(-b)/(2.0*a);
      printf("\nRaíz real doble, rrd= %f", rrd);
     }
     break;
 default:
     {
      printf("\nError catastrófico - tipo de ecuación cuadrática desconocido!");
     }
     break;
 }
 printf("\n\nTerminación normal del programa.\n");
 return 0;
}
Comentarios .- El algoritmo empleado es el sobradamente conocido para la resolución de ecuaciones de segundo grado. Para hace más comprensible el programa se ha creado un tipo de datos enumerado, enum tipos_de_raices , para luego declarar una variable ( tipo ) de este tipo de datos. De este modo se puede hablar de manera comprensible de los distintos tipos de soluciones; tipo se utiliza como expresión de control en la sentencia switch del programa.
Para calcular el valor de tipo se ha recurrido a dos operadores ?: anidados; lo mismo podría hacerse con sentencias if()-else , quizá algo más voluminosas aunque el resultado sería exactamente el mismo. A fin de cuentas, se trata de dar el valor correcto a tipo , en función del signo del discriminante.
Para abreviar el programa se ha utilizado una sola sentencia switch . Nada impide, desde luego, utilizar dos sentencias switch distintas, una para el cálculo y otra para la impresión; de hecho, el programa resultante resulta más claro.

Ejercicio 2 .- Construir un programa que realice diferentes acciones en función de la selección efectuada por el Usuario. El programa deberá mostrar un menú en pantalla; recibirá instrucciones del usuario y ejecutará las acciones correspondientes. Para salir de programa, el usuario seleccionará la opción oportuna.
/*
  Esta es una versión muy primitiva del programa núcleo, sin utilizar subprogramas.
  Este ejercicio muestra la aplicación de la sentencia switch,
  con especial antención a break y default.

  Esta versión del programa núcleo es apenas utilizable,
  es tan solo un ejemplo de aplicación de la sentencia switch.
 */

/*
  Basado en nucleosp.pas
*/

#include<stdio.h>



void main(void)
{
  int fin;                    /* control de iteración del programa */
  char instruccion;           /* dada por el usuario               */
  char menu[80];              /* que muestra el programa           */
  char opcionesvalidas[80];   /* que admite el programa            */

  /* Iniciaciones generales */

  fin = 0;                        /* Acabamos de arrancar, no hay que salir */
  strcpy(menu,"O)perar S)alir");  /* Esto es lo que mostrará el menú        */
  strcpy(opcionesvalidas,"OSQ");  /* Estas son las opciones admisibles      */

  do {

    do {
      printf("%s ", menu);
      scanf("%c%*c",&instruccion);/* Se descarta el \n del usuario  */
      instruccion=toupper(instruccion);
    } while (!strchr(opcionesvalidas,instruccion));

    /*                     "dispatcher"               */
    switch(instruccion)
    {
      case 'S': {
              fin = 1;
              puts("Nos vamos!\n");
            }
            break;
      case 'O': {
              puts("Estamos operando.\n");
            }
            break;
      default : {
              puts("Instrucción incorrecta.\n");
            }
            break;
    }

  }
  while (!fin);

  puts("\n\nThat's all folks!\n");
}
/*
  COMENTARIOS

Este programa admite "macros". En efecto, escriba varias veces la letra
"o" y observará que se producen varias llamadas al bloque correspondiente
a operar. Si hubiera más de una letra correspondiente a una operación,
se desencadenarían sucesivamente todas las operaciones.
Este programa es posiblemente el intérprete más pequeño posible.

¿Qué ocurre si las instrucciones se leen de un fichero (y no del teclado)?
¿Qué ocurre si en lugar de caracteres se leen cadenas completas (gets)?
¿Qué ocurre si se analizan esas cadenas, en busca de palabras reservadas
 (input, print,load, save, list, run...?

  ¿Cómo funciona command.com, o cualquier otro intérprete de líneas?
  ¿Se entiende el tratamiento de un fichero .BAT?
*/



Ejercicios propuestos

  1. Ejercicio 0404r01.- Construir un programa basado en funciones que muestre un menú y admita opciones (mediante caracteres).
  2. Ejercicio 0404r02.- Construir un programa que muestre una barra de menús. El cada menú llevará a un modo dotado de distintas opciones seleccionables.
  3. Ejercicio 0404r03.- Construir un programa que muestre una barra de menús; cada menú conducirá a un modo (con un nuevo menú). El programa leerá todas las opciones de un archivo de texto previamente creado.

  4. Ejercicio 0404r04.- Construir un programa que ofrezca un cierto número de opciones mediante un menú. Al seleccionar una opción se ejecutará una función cuyo puntero se encuentra en una lista.

  5. Ejercicio 0404r05.- Construir un programa que ofrezca un cierto número de opciones mediante una barra de menús. Al seleccionar una opción se ejecutará una función cuyo puntero se encuentra en una tabla.

  6. Ejercicio 0404r06.- Construir un programa, basado en los conceptos anteriores, que ofrezca las opciones habituales de una pila. Emplear una estructura lineal dotada de un número finito de elementos.

  7. Ejercicio 0404r07.- Construir un programa, basado en los conceptos anteriores, que ofrezca las opciones habituales de una cola. Emplear una estructura lineal dotada de un número finito de elementos.

  8. Ejercicio 0404r08.- Considérese una lista circular de registros, basada en una estructura lineal finita. Se pide escribir un programa que ofrezca e implemente las opciones habituales de este tipo de estructuras.