Programación en C/Instrucciones de control

De Wikilibros, la colección de libros de texto de contenido libre.
← Expresiones Instrucciones de control Uso de funciones →



Como ya se ha mencionado, C es un ejemplo de programación estructurada. En este tipo de programación, es necesario contar con ciertas estructuras que permitan controlar el flujo del programa, es decir, tomar decisiones y repetir acciones.

La estructura condicional if ... else[editar]

En la gran mayoría de los programas será necesario tomar decisiones sobre qué acciones realizar. Esas decisiones pueden depender de los datos que introduzca el usuario, de si se ha producido algún error o de cualquier otra cosa.

La estructura condicional if ... else es la que nos permite tomar ese tipo de decisiones. Traducida literalmente del inglés, se la podría llamar la estructura "si...si no", es decir, "si se cumple la condición, haz esto, y sino, haz esto otro".

Un ejemplo sencillo sería el siguiente (no se trata de un programa completo, sino tan sólo una porción de código):

	if (edad < 18) 
		printf("No puedes acceder.\n");
	else
		printf("Bienvenido.\n");

Este código de ejemplo dice que si el valor de la variable edad es menor que 18 se imprimirá "No puedes acceder.\n", mientras que en caso contrario se imprimirá "Bienvenido.\n".

Como se ve en el ejemplo, la estructura de un condicional es bastante simple:

	if (condición) {
		sentencias_si_verdadero;
	} else {
		sentencias_si_falso;
	}

La condición, encerrada entre paréntesis, es una expresión que puede dar como resultado 0 (interpretado como falso) o cualquier valor distinto de 0 (interpretado como verdadero). Cuando la condición sea verdadera, se ejecutarán las sentencias dentro del primer bloque de código, cuando la condición sea falsa, se ejecutarán las sentencias del segundo bloque de código. Las expresiones y valores de tipo verdadero/falso son también llamados valores lógicos o booleanos.

La indentación o sangría (los espacios al comienzo de las líneas) no es necesaria, pero ayuda a la claridad del código. La utilización de las llaves {...} es obligatoria cuando se quiere utilizar más de una instrucción por bloque, y optativa cuando sólo se quiere escribir una instrucción. Por claridad, sin embargo, es recomendable utilizarlas aún cuando sólo vaya a haber una instrucción.

El bloque del else es opcional. Si no se lo encuentra, sólo se realizará la acción correspondiente al bloque if.

A continuación, un ejemplo con una función, que devuelve el mayor de dos números:

int mayor(int a, int b)
{
	if (b > a) {
		return b;
	}// No posee especificación de la parte "else", ya que no es necesaria.  
       	return a; // Finaliza la función retornando el valor de "a".
}

Operadores de comparación[editar]

El símbolo > visto en el último ejemplo es un operador, que en este caso compara dos números enteros y devuelve verdadero si el primero es mayor, falso en caso contrario.

A continuación un listado de los posibles operadores de comparación en C y su significado.

Operadores de Comparación
Operador Significado
< estrictamente menor que
> estrictamente mayor que
<= menor o igual que
>= mayor o igual que
== igual a
!= distinto de


Teniendo en cuenta que en C se toma como falso el valor 0, y como verdadero cualquier otro valor, una práctica común es expresar condiciones sin utilizar ningún operador:

float division(int dividendo, int divisor)
{
	if (divisor) {
		return dividendo / divisor;
	} else {
		printf ("No se puede dividir por cero\n");
		return 0;
	}
}

En este caso, la expresión (divisor) es equivalente a (divisor != 0).

Operadores lógicos[editar]

Los operadores && ("y"), || ("o") y ! ("no") son operadores lógicos. Permiten operar con expresiones lógicas para generar expresiones más complejas.

Por ejemplo: determinar si un año es bisiesto o no. Los años son bisiestos si son divisibles por 4, pero no si son divisibles por 100, a menos que también sean divisibles por 400.

	if ( (!(a % 4) && (a % 100)) || !(a % 400) )  {
		printf("es un año bisiesto.\n");
	} else {
		printf("no es un año bisiesto.\n");
	}

En realidad, teniendo en cuenta la prioridad de los operadores utilizados, podemos simplificar la expresión anterior del siguiente modo:

	if ( !(a % 4) && (a % 100) || !(a % 400) )  {
		printf("es un año bisiesto.\n");
	} else {
		printf("no es un año bisiesto.\n");
	}

Además, como a cada rama del if le sigue una única instrucción, podemos expresar la expresión anterior del siguiente modo:

	if ( !(a % 4) && (a % 100) || !(a % 400) )  
		printf("es un año bisiesto.\n");
	else 
		printf("no es un año bisiesto.\n");


En este caso, se utiliza el operador módulo (%), que obtiene el resto de la división entera de un número por otro. Cuando un número es divisible por otro, el resto de su división entera será cero. Siendo que cero es equivalente a falso, y cualquier valor distinto de cero es equivalente a verdadero, podemos usar el operador % para verificar si el número es múltiplo de 4, de 100 o de 400.


Evaluación de cortocircuito[editar]

La evaluación en corto circuito es una característica del lenguaje C que se utiliza para optimizar la ejecución de programas. Consiste en que el programa puede verificar si una expresión es verdadera o falsa antes de haber evaluado toda condición.

Por ejemplo, si se tiene una condición como la siguiente:

	if ((a > 2) || (b < 4)) {
		...
	}

Al ejecutarse el programa, se evaluará primero si a > 2. En el caso en que sea verdadero, no continuará con la siguiente condición, ya que el resultado será de cualquier modo verdadero.

De la misma forma, si la condición fuera:

	if ((a > 2) && (b < 4)) {
		...
	}

En este caso, si no se cumple que a > 2, no se evaluará la siguiente condición, ya que el resultado será falso de todos modos.

Esta característica no tiene demasiada importancia al comenzar a programar, pero facilitará ciertas operaciones y optimizaciones en programas avanzados.


La estructura condicional abierta y cerrada switch ... case[editar]

La estructura condicional switch ... case se utiliza cuando queremos evitarnos las llamadas escaleras de decisiones. La estructura if nos puede proporcionar, únicamente, dos resultados, uno para verdadero y otro para falso. Una estructura switch ... case, por su parte, nos permite elegir entre muchas opciones. Ejemplo:

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

int main(void) {
	int dia;

	printf("¿Qué número de día de la semana es?");
	scanf("%i",&dia);

	switch(dia) {
		case 1 :
			printf("Lun, Lunes");
			break;
		case 2 :
			printf("Mar, Martes");
			break;
		case 3 :
			printf("Mier, Miercoles");
			break;
		case 4 :
			printf("Jue, Jueves");
			break;
		case 5 :
			printf("Vie, Viernes");
			break;
		case 6 :
			printf("Sab, Sabado");
			break;
		case 7 :
			printf("Dom, Domingo");
			break;
		default :
			printf("No existe");
}
return 0;
}

La estructura anterior, de realizarse con sentencias if, necesitaría cuatro de ellas, resultando un enorme bloque muy difícil de leer. En la mayoría de los casos, además, la sentencia switch proporciona una ganancia en velocidad del código, pues permite al compilador trabajar en base a que se trata de una decisión múltiple para una única variable, cosa que con sentencias if el compilador no tiene por qué detectar.

Como vemos, para cada valor de la variable se ejecuta un bloque de sentencias distinto, en el que no necesitamos llaves. Hay un caso especial, default, que se ejecuta si ningún otro corresponde, y que no es necesario poner. Es, en todo, equivalente al bloque else de una sentencia if.

Las sentencias break son muy importantes, ya que el comportamiento normal de un bloque switch es ejecutarlo todo desde la etiqueta case que corresponda hasta el final. Por ello, si no queremos que se nos ejecute más de un bloque, pondremos sentencias break al final de cada bloque excepto el último.

Es decir, las etiquetas case son puntos de entrada de la ejecución, y no implican que al acabarse el bloque case la ejecución salte al final del bloque switch. Las etiquetas case siguientes a la que hemos utilizado para entrar son, sencillamente, ignoradas.

A la ausencia de sentencias break se le llama, en ocasiones, "dejar caer la cascada switch".

El bucle while[editar]

El bucle while sirve para ejecutar código reiteradas veces.

while (/*condicion*/) {
	/* Código */
}

La condición debe de ser una expresión lógica, similar a la de la sentencia if. Primero se evalúa la condición. Si el resultado es verdadero, se ejecuta el bloque de código. Luego se vuelve a evaluar la condición, y en caso de dar verdadero se vuelve a ejecutar el bloque. El bucle se corta cuando la condición da falso.

Ejemplo: imprimir los números de 0 a 99:

int i = 0;
while (i < 100) {
	printf("%d\n", i);
	i = i + 1;
}

Inicialmente se declara que la variable i tiene un valor de 0. Al iniciar el bucle, se cumple la condición i < 100, por lo que se procede a la instrucción de imprimir dicho número (cero, en el caso inicial). Posteriormente i cambiará su valor de uno en uno por la instrucción i = i + 1 y seguidamente dicho valor nuevo, será evaluado en la condicion while hasta que i llegue al valor 100, donde debido a la condicional, éste será un valor falso, dando fin al código.

El bucle for[editar]

El bucle for es un bucle muy flexible y a la vez muy potente ya que tiene varias formas interesantes de implementarlo, su forma más tradicional es la siguiente:

for (/* inicialización */; /* condición */; /* incremento */) {
	/* código a ejecutar */
}

Inicialización: en esta parte se inicia la variable que controla el bucle y es la primera sentencia que ejecuta el bucle. Sólo se ejecuta una vez ya que solo se necesita al principio del bucle.

Expresión condicional: al igual que en el bucle while, esta expresión determina si el bucle continuará ejecutándose o no.

Incremento: es una sentencia que ejecuta al final de cada iteración del bucle. Por lo general, se utiliza para incrementar la variable con que se inicio el ciclo. Luego de ejecutar el incremento, el bucle revisa nuevamente la condición, si es verdadera tiene lugar una ejecución más del cuerpo del ciclo, si es falsa se termina el ciclo y así.

Aquí se muestra el mismo ejemplo visto para el bucle while, pero implementado con un bucle for:

int i;
for (i=0; i < 100; i = i + 1) {
	printf("%d\n", i);
}

Nota: En C, la sentencia i = i + 1 puede escribirse en forma más reducida como i++. Esta forma se utiliza más comúnmente en el bucle for:

int i;
for (i=0; i < 100; i++) {
	printf("%d\n", i);
}

El bucle do...while[editar]

El bucle do...while es un bucle que, por lo menos, se ejecuta una vez. Do significa literalmente "hacer", y while significa "mientras".

Su forma es esta:

do {
	/* CODIGO */
} while (/* Condición de ejecución del bucle */)

Os muestro un ejemplo sencillo de uso:

int aleatorio;
do {
	aleatorio = rand();
} while (aleatorio != 25);

La verdad es que este ejemplo puede resultar un poco absurdo, pero es bastante intuitivo. El código del bucle asigna un valor aleatorio a la variable definida anteriormente, y mientras esa variable no tenga el valor 25, el bucle sigue ejecutándose.

La sentencia goto[editar]

La sentencia goto sirve para indicar al programa que continue ejecutándose desde la línea de código indicada. Su sintaxis es más o menos así:

/* Código */
ETIQUETA:
/* Código */
goto ETIQUETA;
/* Código */

Así, cuando se ejecute la sentencia goto, el programa "saltará" y continuará su ejecución a partir de la etiqueta marcada.

Como se puede observar se puede usar para crear un bucle, o para ir a una parte del código u otra si se combina con una sentencia if...else. Pero por lo general puede obtenerse el mismo efecto utilizando los bucles anteriormente vistos.

Por eso, la sentencia goto es poco aceptada por la comunidad de programadores, pues puede provocar que se hagan programas un poco "sucios" y confusos. Sólo en ocasiones muy excepcionales será recomendado el uso del goto al crear iteraciones muy complejas. Sin embargo, con el pasar de los años este comando ya ha quedado prácticamente descartado del lenguaje de los programadores.


← Expresiones Instrucciones de control Uso de funciones →