Sentencias de Decisión en C (switch)

Author
Por Darío Rivera
Publicado el en Lenguaje C

En un post anterior hemos visto las Sentencias de Decisión en C (if, else), el día de hoy terminaremos el tema con la sentencia Switch. Las sentencias de control permiten ejecutar bloques de código basados en una condición. En el caso específico de la sentencia switch, estas condiciones están dadas por un conjunto de valores o expresiones.

Condiciones mutuamente excluyentes

Al igual que las sentecias if, else podemos construir una serie de condiciones las cuáles permitirán que solamente un bloque de código se ejecute. Veamos el siguiente ejemplo.

#include <stdio.h>
#include <string.h>

int main(void)
{
    int day = 3;
    char dayName[20];

    switch (day) {
        case 1:
            strcpy(dayName, "Monday");
            break;
        case 2:
            strcpy(dayName, "Tuesday");
            break;
        case 3:
            strcpy(dayName, "Wednesday");
            break;
        case 4:
            strcpy(dayName, "Thursday");
            break;
        case 5:
            strcpy(dayName, "Friday");
            break;
        case 6:
            strcpy(dayName, "Satudary");
            break;
        case 7:
            strcpy(dayName, "Sunday");
            break;
        default:
            strcpy(dayName, "Invalid day");
            break;
    }

    printf("%s\n", dayName);
}

Al ejecutar este código se obtiene el siguiente resultado.

Wednesday

En este caso y como es de esperarse, el bloque switch verificará solamente las tres primeras condiciones ya que como day es igual a 3 entonces se cumple case 3 y ese bloque específico es ejecutado. Con la instrucción break se impide que los bloques subsecuentes se ejecuten. Nota también que hemos agregado un bloque default. Aunque este bloque no es obligatorio, es una buena práctica considerar un caso por defecto siempre que sea posible. Este bloque se ejecutará siempre que no se cumpla ninguna condición.

Ya que el bloque default no necesariamente debe ir de último, es recomendable también agregar la sentencia break en él. Esto previene que si se agrega una condición después del bloque default no haya comportamientos inesperados.

Condiciones no excluyentes

A diferencia de las sentencias if, else, la sentencia switch permite que el flujo de ejecución pueda seguir por varios caminos y no solamente por uno. Veamos el siguiente ejemplo.

#include <stdio.h>

int main(void)
{
    int years = 4;
    int bonus = 0;

    switch (years) {
        case 5:
            bonus += 1000;
        case 4:
            bonus += 0;
        case 3:
            bonus += 500;
        case 2:
            bonus += 0;
        case 1:
            bonus += 100;
    }

    printf("Accumulated Bonus: %d\n", bonus);
}

Al ejecutar este código se obtiene el siguiente resultado.

Accumulated Bonus: 600

El objetivo de este programa es calcular el bono acumulado que tendría una persona que trabaja en una empresa en la cuál al primera año recibe 100 dólares, al tercer año recibe 500 y al quinto año recibe 1000. Como puedes notar una persona que lleva cuatro años habrá obtenido un bono acumulado de 600 dólares tal y como lo muestra la salida del programa.

Esto es así, dado que como habrás notado la condición entra en el statement case 4 el cuál suma a la variable bonus 0 USD. Dado que este bloque no tiene break el programa seguirá ejectando los demás bloques sin importar el valor de la expresión de las sentencias case. Es decir, habrá sumado en total 0 + 500 + 0 + 100. Ten en cuenta que el bloque switch terminará su ejecución en la primera sentencia break que encuentre.

Otro aspecto a tener en cuenta es el orden de las sentencias case, ya que éstas se ejecutan de arriba hacia abajo como es usual. En el anterior ejemplo, después de que la condición del case 4 se cumple, todas las demás expresiones subsecuentes se ejecutan.

Agrupación de condiciones

Una de las grandes ventajas de la sentencia case es que se pueden agrupar condiciones con el mismo comportamiento. Supongamos ahora que el gobierno dará un incentivo a las madres por sus hijos menores de 6 años. Por un hiijo de 1 o 2 años dará un incentivo de 100 USD, por un niño de 3 años dará 500, por un niño de 4 dará 1000 y por uno de 5 dará 2000. Dado esto podemos escribir el siguiente programa.

Observa el siguiente programa que modela esta situación. Presta especial atención en el primer requerimiento del problema en donde de acuerdo a dos condiciones se da un incentivo de 100 USD.

#include <stdio.h>

int main(void)
{
    int age = 1;
    int bonus = 0;

    switch (age) {
        case 1:
        case 2:
            bonus = 100;
            break;
        case 3:
            bonus = 500;
            break;
        case 4:
            bonus = 1000;
            break;
        case 5:
            bonus = 2000;
            break;
    }

    printf("Incentive: %d\n", bonus);
}

Observa que el cambio es tan sutil como elegante. Dado que el primer y segundo año se recibe lo mismo por hijo no hace falta colocar la misma condición en el bloque case 2. La salida del programa será la siguiente tomando la variable age como la edad del menor.

Incentive: 100

La agrupación debe darse siempre por sentencias case y no por el operador lógico OR (||). Es por esta razón que no podemos agrupar condiciones de la siguiente forma, aún cuando es sintácticamente correcto.

case 1 || 2:

La evaluación de la anterior expresión dará como resultado true, o lo que es lo mismo 1. Debido a esto, solo funcionará para case 1. Otros lenguajes como PHP sí aceptan esta sintaxis y es similar a agrupar por case.

La expresión en la sentencia case debe ser una constante entera, una constance de un solo carácter o una expresión constante, en cuyo caso, el valor resultante tiene que ser entero. Esto quiere decir también que debe ser evaluada en tiempo de compilación.

Supongamos agregamos la siguiente restricción a nuestro requerimiento. Si el hijo es varón y tiene dos años, no se dará el incentivo.

Ya que no es posible agregar en una expresión case una variable, ya que esta se evalúa en tiempo de ejecución, y no de compilación, el siguiente fragmento de código sería incorrecto.

int main(void)
{
    int age = 1;
    int bonus = 0;
    char gender = 'M';

    switch (age) {
        case 1:
        // esto es un error!
        case (gender == 'F') ? 2 : 0:
            bonus = 100;
            break;
        case 3:
            ...
    }
}

El código anterior lanzará un error en la compilación. Sin embargo, esto podría arreglarse cambiando la variable gender por una constante macro.

#include <stdio.h>
#define GENDER 'M'

int main(void)
{
    int age = 2;
    int bonus = 0;

    switch (age) {
        case 1:
        case (GENDER == 'F') ? 2 : 0:
            bonus = 100;
            break;
        case 3:
            bonus = 500;
            break;
        case 4:
            bonus = 1000;
            break;
        case 5:
            bonus = 2000;
            break;
    }

    printf("Incentive: %d\n", bonus);
}

Este código funciona tal y como esperamos pero es poco escalable, ya que solo sirve para el caso cuando el género es masculino, o bien femenino si se cambia a 'F' la constante. La idea de este ejemplo, es mostrarte que son válidas las expresiones dentro de la sentencia case siempre que sean constantes.

Inicialización de variables

Como pudiera pensarse es posible inicializar variables dentro de los bloques case. Hay que tener en cuenta que estas variables no estarán disponibles por fuera del bloque switch.

switch (age) {
    case 1:
        bonus = 100;
        break;
    case 2:
        char gender;
        gender = 'F';
        if (gender == 'F')
            bonus = 100;
        break;
...

Para que una declaración de variable esté disponible durante todo el bloque switch, se puede realizar justo antes del primer case. Sin embargo, solo es posible declarala, cualquier incialización será ignorada y debe ser realizada dentro de los bloques case.

switch (age) {
    char gender;
    case 1:
        bonus = 100;
        break;
    case 2:
        gender = 'F';
        if (gender == 'F')
            bonus = 100;
        break;
...

Acerca de Darío Rivera

Author

Application Architect at Elentra Corp . Quality developer and passionate learner with 10+ years of experience in web technologies. Creator of EasyHttp , an standard way to consume HTTP Clients.

LinkedIn Twitter Instagram

Sólo aquellos que han alcanzado el éxito saben que siempre estuvo a un paso del momento en que pensaron renunciar.