Hasta ahora todo lo que hemos ido viendo durante éste curso básico de C ha
ido encaminado a realizar programas lineales, con un proceso de ejecución
inalterable donde se realizan todas y cada una de las instrucciones sin
necesidad de modificar nada. En este capítulo vamos a ver las estructuras de
control de flujo disponibles en C, con las que podremos alterar el curso de la
ejecución según las necesidades que tengamos.
Foto tomada de freedigitalphotos.net |
Como ya hemos definido en anteriores artículos, existen dos tipos de
estructuras de control de flujo, las selectivas y las iterativas, gracias a
éstas podremos modificar el flujo de ejecución, haciendo que se ejecuten unas
instrucciones u otras (selectivas) o ejecutar instrucciones un número repetido
de veces (iterativas).
Estructuras de control de flujo selectivas.
1 – if.
La estructura de selección fundamental es el if, gracias a ésta podremos
evaluar una expresión con el fin de que si el resultado de esa evaluación es un
booleano TRUE la sentencia o sentencias incluídas en él se ejecutarán, en caso
contrario el flujo de ejecución no realizará ninguna de esas instrucciones y
seguirá su curso.
Un if tiene la siguiente estructura:
if (expresión a evaluar) {
sentencias;
}
Es importante que respetemos la sintaxis en todo momento, los paréntesis
son esenciales para que el programa sepa que sentencia evaluar.
En caso de que dentro del if sólo haya una sentencia, las llaves podemos
omitirlas.
if (expresión a evaluar)
sentencia;
Pero cuidado, en caso de que el if contenga varias sentencias debemos
obligatoriamente encerrarlas en las llaves, si no lo hacemos sólo se ejecutará
la primera.
No es lo mismo esto:
if (expresión a evaluar) {
sentencia1;
sentencia2;
sentencia3;
sentencia4;
}
Que esto:
if (expresión a evaluar)
sentencia1;
sentencia2;
sentencia3;
sentencia4;
Como expresión a evaluar podemos utilizar variables, funciones, operadores
de comparación, booleanos o concatenar varias expresiones. No se permite la
asignación de valores.
if (a > b) {
printf(“a es mayor que
b\n”);
}
if (a + b == c) {
printf(“a + b es igual a c\n”);
}
if (a + b == c && a > b) {
printf(“a + b es igual a c y además a es mayor que
b\n”);
}
if (a + b == c || a > b) {
printf(“o bien a + b es igual a c o bien a es
mayor que b\n”);
}
Pruebe a realizar éste código en el IDE:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int
a,b,c;
a=2;
b=0;
c=3;
if (a
> b) {
printf("a es mayor que b\n");
}
if (a
+ b == c) {
printf("a + b es igual a c\n");
}
if (a
+ b == c && a > b) {
printf("a + b es igual a c y además a es mayor que b\n");
}
if (a
+ b == c || a > b) {
printf("o bien a + b es igual a c o bien a es mayor que b\n");
}
system("PAUSE");
return 0;
}
2 – if...else
Hay momentos en los que necesitaremos que nuestro programa ejecute unas
acciones u otras dependiendo de la valoración de la expresión en el if, para
eso tenemos a nuestra disposición la palabra reservada else, con else podemos
determinar qué código se va a ejecutar en caso de que la valoración de la
expresión del if sea FALSE.
El else no puede ser utilizado sin un if, tampoco puede utilizarse junto a
una expresión para valorar (éste caso lo veremos más adelante). La estructura
del else es la siguiente:
if (expresión a evaluar) {
sentencias;
}
else {
sentencias;
}
Así si la evaluación de la expresión da como resultado FALSE el programa
procederá a ejecutar el código que haya dentro del else.
La regla de las llaves del if también se aplican al else.
No es lo mismo:
if (expresión a evaluar) {
sentencia1;
}
else {
sentencia2;
sentencia3;
sentencia4;
}
Que esto:
if (expresión a evaluar) {
sentencia1;
}
else
sentencia2;
sentencia3;
sentencia4;
La eliminación de las llaves, insisto, es completamente opcional, se puede
aplicar como más cómodo nos encontremos, pudiendo hacer todas estas opciones:
if (expresión a evaluar) {
sentencia1;
}
else {
sentencia2;
}
if (expresión a evaluar)
sentencia1;
else {
sentencia2;
}
if (expresión a evaluar) {
sentencia1;
}
else
sentencia2;
if (expresión a evaluar)
sentencia1;
else
sentencia2;
Pruebe a realizar éste código en el IDE:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int
a,b,c;
a=2;
b=0;
c=3;
if (a
> b)
printf("a es mayor que b\n");
else
{
printf("a no es mayor
que b\n");
}
if (a
+ b == c) {
printf("a + b es igual a c\n");
}
else
printf("a + b no es
igual a c\n");
if (a
+ b == c && a > b)
printf("a + b es igual a c y además a es mayor que b\n");
else
printf("a + b no es
igual a c y además a es mayor que b\n");
if (a + b == c || a > b) {
printf("o bien a + b es igual a c o bien a es mayor que b\n");
}
else
{
printf("ni a + b es
igual a c ni a es mayor que b\n");
}
system("PAUSE");
return 0;
}
¿Sencillo, verdad? Con el uso del if tenemos muchas más posibilidades de
cara a controlar el flujo del programa, con else además podremos controlar las
evaluaciones incorrectas, las posibilidades para ir realizando cada vez mejores
programas van creciendo.
3 – if anidado.
En el programa anterior teníamos el siguiente código:
if (a + b == c && a > b)
printf("a + b es igual a c y además a es
mayor que b\n");
else
printf("a + b no es igual a c y además a es
mayor que b\n");
Si lo compilábamos y ejecutábamos la salida era correcta debido a que la
variable a tiene el valor 2 y la variable b tiene el valor 0, a + b nunca puede
ser 3, y si además vemos la segunda expresión tenemos que, efectivamente, a es
mayor que b, por lo tanto el mensaje del else "a + b no es igual a c y
además a es mayor que b" es correcto. ¿Pero que pasaría si le diésemos a
la b el valor 4? Ya no sería correcto y nuestro programa funcionaría mal.
Para ello es posible utilizar estructuras if dentro de otras estructuras
if, a esto se le conoce como if anidado. La estructura de un if anidado sería
la siguiente:
if (expresión a evaluar) {
if (expresión a evaluar) {
sentencias;
}
}
else {
sentencias;
}
Cabe resaltar que las estructuras if podemos anidarlas cuantas veces sean
necesarias, dentro del bloque if o dentro del bloque else, no existen límites.
Ahora veamos la estructura del if anidado pero suprimiendo las llaves del
primer if.
if (expresión a evaluar)
if (expresión a evaluar) {
sentencias;
}
else {
sentencias;
}
¿Es ésta estructura igual que la anterior? En teoría tiene sentido ¿no?
Tenemos un if que lo único que va a hacer es ejecutar dentro otra sentencia if,
tenemos un solo elemento por lo que no es necesario poner llaves, además el
aspecto es similar, el else se ejecutará si el primer if resulta en FALSE.
Además de todo esto el else sigue perteneciendo al primer if, ¿correcto? Pues
no.
C trabaja con una regla muy sencilla, el else pertenece al último if que no
tenga else. Es decir, que si indentamos correctamente la estructura anterior
quedaría así:
if (expresión a evaluar)
if (expresión a evaluar) {
sentencias;
}
else {
sentencias;
}
Hemos perdido completamente la estructura que teníamos definida debido a la
necesidad de ahorrarnos unas llaves, pues precisamente esto será una de las
cosas que C tendrá en cuenta a la hora de decidir qué else pertenece a qué if.
if (expresión a evaluar) {
if (expresión a evaluar) {
sentencias;
}
}
else {
sentencias;
}
Ahora vuelve a estar todo como antes, si el primer if resulta positivo
comenzará a evaluar el segundo if, y si éste sale positivo ejecutará las
sentencias pertinentes, en caso contrario no hará absolutamente nada, el
programa se saldrá del primer if y continuará con el código siguiente, el
primer else no se tendrá en cuenta porque la evaluación del primer if ha sido
TRUE, no le afecta en nada que la evaluación del segundo if sea FALSE.
Al igual que anidamos if podemos anidar if...else:
if (expresión a evaluar) {
if (expresión a evaluar) {
sentencias;
}
else {
sentencias;
}
}
else {
sentencias;
}
También es posible anidar if...else dentro de un else:
if (expresión a evaluar) {
if (expresión a evaluar) {
sentencias;
}
else {
sentencias;
}
}
else {
if (expresión a evaluar) {
sentencias;
}
else {
sentencias;
}
}
Podemos poner tantas estructuras como queramos y de la forma que queramos:
if (expresión a evaluar) {
if (expresión a evaluar) {
sentencias;
}
else {
if
(expresión a evaluar) {
sentencias;
}
else {
sentencias;
}
}
}
else {
if (expresión a evaluar) {
sentencias;
}
else {
sentencias;
}
if (expresión a evaluar) {
sentencias;
}
else {
}
if (expresión a evaluar) {
sentencias;
}
else {
}
}
Ahora volvamos al trozo de código del apartado anterior, el que hemos visto
que está mal:
if (a + b == c && a > b)
printf("a + b es igual a c y además a es
mayor que b\n");
else
printf("a + b no es igual a c y además a es
mayor que b\n");
Ahora con los if anidados podemos solucionar esto, quedando algo así:
if (a + b == c && a > b) {
printf("a + b es igual a c y además a es
mayor que b\n");
}
else {
if (a > b) {
printf("a + b no es igual a c y
además a es mayor que b\n");
}
else {
printf("a + b no es igual a c y
además a no es mayor que b\n");
}
}
Controlado el problema ¿verdad? Pues no, aún queda un caso que contemplar.
Imaginemos que a vale 0 y b vale 3, la condición a + b == c se cumple pero la otra no, por lo
tanto iría al else y después de evaluar llegaría a la conclusión de que "a
+ b no es igual a c y además a no es mayor que b", esto es incorrecto,
debemos arreglarlo también usando nuevos if y else.
Una posible solución para éste problema es la siguiente:
if (a + b == c && a > b) {
printf("a + b es igual a c y además a es
mayor que b\n");
}
else {
if
(a + b == c) {
printf("a + b es igual a
c");
}
else {
printf("a + b no es igual a
c");
}
printf(" y además ");
if (a > b) {
printf("a es mayor que
b\n");
}
else {
printf("a no es mayor que
b\n");
}
}
Combinando los posibles usos del printf y omitiendo los primeros \n para
evitar saltos de línea ha quedado un código que parece bastante seguro y que
controla todos los casos.
4 – else if.
Existen ocasiones en las que la estructura if...else se puede quedar corta
para nuestras necesidades, por eso en C disponemos de otra forma de estructura,
la que utiliza el else if.
Con else if se puede evaluar una nueva condición en caso de que la
valoración del primer if sea FALSE, se ejecuta antes de else, dejando a éste
como última opción a tener en cuenta.
La estructura sería la siguiente:
if (condición) {
sentencias;
}
else if (condición) {
sentencias;
}
else {
sentencias;
}
El else if puede hacer uso de las reglas de llaves que ya hemos visto, se
pueden obviar en caso de ser una única instrucción a ejecutar, no obstante
también hay que tener en cuenta la regla de pertenencia del else que ya hemos
citado antes (un else pertenece al último if abierto), también se aplica al
else if. Además else if tiene un ventaja con respecto a los demás, se pueden
poner tantos como se necesite.
Una posible forma de utilizar el else if para el ejemplo anterior sería la
siguiente:
if (a + b == c && a > b) {
printf("a + b es igual a c y además a es
mayor que b\n");
}
else if (a + b == c) {
printf("a + b es igual a c");
}
else if (a > b) {
printf("a es mayor que b\n");
}
else {
printf("a + b no es igual a c");
printf(" y además ");
printf("a no es mayor que b\n");
}
Con éste último código se termina de contemplar el caso ya visto dentro del
ejercicio de inicio del post.
5 – Sentencia switch.
Existen ocasiones en las que necesitaremos ejecutar acciones en función del
valor de una expresión o del valor de una variable concreta, esto hacerlo
mediante if puede resultar bastante tedioso, se llenaría el código de if
anidados y si cometemos algún error de indentación o de llaves puede resultar
que nuestro código no funcione correctamente, para eso existe la sentencia
switch.
La sentencia switch realiza esta acción perfectamente, ahorrando tiempo de
codificación y obteniendo control sobre los resultados, su estructura es la
siguiente:
switch (expresión) {
case valor1:
sentencias
break;
case valor2:
sentencias
break;
...
default:
sentencias
break;
}
Un ejemplo bastante común de uso de las sentencias swtich es la creación de
un menú de opciones, mediante la cual al elegir una opción se ejecutará una
serie de instrucciones. Pruebe a ejecutar el siguiente código en su IDE:
#include <stdio.h>
int main(void)
{
int opcion;
printf ("Elija una opción: \n");
printf ("1) Inicio\n");
printf ("2) Salir\n");
scanf ("%d", &opcion);
switch (opcion) {
case 1:
printf ("Estas en el
inicio\n");
break;
case 2:
printf ("Has salido del
programa\n");
break;
default:
printf ("Opción no
válida\n");
break;
}
return 0;
}
La sentencia break se utiliza para que el flujo de ejecución se salga de
toda la sentencia switch y siga ejecutando las sentencias siguientes.
La opción default es completamente opcional y su uso sería similar al del
else de la estructura if, es decir, cuando ninguna de las opciones contempladas
se corresponde con la de la variable o expresión que se está valorando.
Conclusiones.
Las sentencias de control de flujo condicionales nos sirven para contemplar
casos en los que necesitamos tener control sobre las variables y expresiones,
evitar errores y tomar decisiones. En el ámbito de los videojuegos es esencial
tener controlados todos los casos posibles ante la amplia libertad que le damos
al jugador con el personaje protagonista.
Con esto terminamos el capítulo de estructuras de control de flujo
condicionales, el próximo día veremos las estructuras de control de flujo
iterativas o repetitivas, veremos cómo podemos hacer que una acción o acciones
se ejecuten un número determinado de veces. Con eso tendremos los elementos
necesarios para ir construyendo algún juego simple, el cual haremos también.
¿Qué otras formas de estructuras de control de flujo condicionales conocéis
de otros lenguajes?