3. Estructuras de control
NOTA: Este apartado
está sin
terminar de revisar.
Falta incluir imágenes, añadir colores,
completar el contenido... incluso
releer ;-)
3.1. Estructuras alternativas
3.1.1. If
Vamos a ver cómo podemos comprobar si se cumplen condiciones. La primera construcción que usaremos será "si ... entonces ...". El formato en C es
if (condición) sentencia;
Vamos a verlo con un ejemplo:
/*---------------------------*/
/* Ejemplo en C nº 14: */
/* C014.C */
/* */
/* Condiciones con if */
/*---------------------------*/
#include <stdio.h>
int numero;
main() {
printf("Escribe un número: ");
scanf("%d", &numero);
if (numero>0) printf("El número es positivo.\n");
}
Ejercicios propuestos:
Ø Crear un programa que pida al usuario un número entero y diga si es par.
Ø Crear un programa que pida al usuario dos números enteros y diga si el primero es múltiplo del segundo.
3.1.2. If y sentencias compuestas
La "sentencia" que se ejecuta si se cumple la condición
puede ser una sentencia simple o una compuesta. Las sentencias
compuestas se forman agrupando varias sentencias simples entre llaves (
{ y } ):
/*---------------------------*/
/* Ejemplo en C nº 15: */
/* C015.C */
/* */
/* Condiciones con if (2) */
/* Sentencias compuestas */
/*---------------------------*/
#include <stdio.h>
int numero;
main() {
printf("Escribe un número: ");
scanf("%d", &numero);
if (numero>0)
{
printf("El número es positivo.\n");
printf("Recuerde que también puede usar negativos.\n");
} /* Aqui acaba el "if" */
} /* Aqui acaba el cuerpo del programa */
En este caso, si el número es negativo, se hacen dos
cosas: escribir un un texto y luego... ¡escribir otro!
(Claramente, en este ejemplo, esos dos “printf”
podrían ser uno solo; más adelante iremos encontrando
casos en lo que necesitemos hacer cosas “más serias”
dentro de una sentencia compuesta).
3.1.3. Operadores relacionales: <, <=, >, >=, ==, !=
Hemos visto que el símbolo “>” es el que
se usa para comprobar si un número es mayor que otro. El
símbolo de “menor que” también es sencillo,
pero los demás son un poco menos evidentes, así que vamos
a verlos:
Operador Operación
< Menor que
> Mayor que
<= Menor o igual que
>= Mayor o igual que
== Igual a
!= No igual a (distinto de)
Y un ejemplo:
/*---------------------------*/
/* Ejemplo en C nº 16: */
/* C016.C */
/* */
/* Condiciones con if (3) */
/*---------------------------*/
#include <stdio.h>
int numero;
main() {
printf("Escribe un número: ");
scanf("%d", &numero);
if (numero!=0) printf("El número no es cero.\n");
}
Ejercicio propuesto: Crear un programa que multiplique dos
números enteros de la siguiente forma: pedirá al usuario
un primero número entero. Si el número que se que teclee
es 0, escribirá en pantalla “El producto de 0 por
cualquier número es 0”. Si se ha tecleado un número
distinto de cero, se pedirá al usuario un segundo número
y se mostrará el producto de ambos.
3.1.4. If-else
Podemos indicar lo que queremos que ocurra en caso de que no se cumpla la condición, usando
la orden “else” (en caso contrario), así:
/*---------------------------*/
/* Ejemplo en C nº 17: */
/* C017.C */
/* */
/* Condiciones con if (4) */
/* Uso de else */
/*---------------------------*/
#include <stdio.h>
int numero;
main() {
printf("Escribe un número: ");
scanf("%d", &numero);
if (numero>0) printf("El número es positivo.\n");
else printf("El número es cero o negativo.\n");
}
Podríamos intentar evitar el uso de “else”
si utilizamos un “if” a continuación de otro,
así:
/*---------------------------*/
/* Ejemplo en C nº 18: */
/* C018.C */
/* */
/* Condiciones con if (5) */
/* Esquivando else */
/*---------------------------*/
#include <stdio.h>
int numero;
main() {
printf("Escribe un número: ");
scanf("%d", &numero);
if (numero>0) printf("El número es positivo.\n");
if (numero<=0) printf("El número es cero o negativo.\n");
}
Pero el comportamiento no es el mismo: en el primer caso
(ejemplo 17) se mira si el valor es positivo; si no lo es, se pasa a la
segunda orden, pero si lo es, el programa ya ha terminado. En el
segundo caso (ejemplo 18), aunque el número sea positivo, se
vuelve a realizar la segunda comprobación para ver si es
negativo o cero, por lo que el programa es algo más lento.
Podemos enlazar los “if” usando
“else”, para decir “si no se cumple esta
condición, mira a ver si se cumple esta otra”:
/*---------------------------*/
/* Ejemplo en C nº 19: */
/* C019.C */
/* */
/* Condiciones con if (6) */
/* if encadenados */
/*---------------------------*/
Fundamentos de programación en C, por Nacho Cabanes
Revisión 0.05 – Página 46
#include <stdio.h>
int numero;
main()
{
printf("Escriba un número: ");
scanf("%d", &numero);
if (numero < 0)
printf("El número es negativo.\n");
else
if (numero == 0)
printf("El número es cero.\n");
else
printf("El número es positivo.\n");
}
3.1.5. Operadores lógicos: &&, ||, !
Estas condiciones se puede encadenar con "y", "o", etc., que se indican de la siguiente forma
Operador Significado
&& Y
|| O
! No
De modo que podremos escribir cosas como
if ((opcion==1) && (usuario==2)) ...
if ((opcion==1) || (opcion==3)) ...
if ((!(opcion==opcCorrecta)) || (tecla==ESC)) ...
Ejercicio propuesto: Crear un programa que pida una letra al usuario y diga si se trata de una vocal.
3.1.6. Cómo funciona realmente la condición en un “if”
Como suele ocurrir en C, lo que hemos visto tiene más miga de la que parece: una condición cuyo resultado sea “falso” nos devolverá un 0, y otra cuyo resultado sea “verdadero” devolverá el valor 1:
/*---------------------------*/
/* Ejemplo en C nº 20: */
/* C020.C */
/* */
/* Condiciones con if (7) */
/* Valor de verdad */
/*---------------------------*/
#include <stdio.h>
int numero;
main() {
printf("2==3 vale %d\n", 2==3); /* Escribe 0 */
printf("2!=3 vale %d\n", 2!=3); /* Escribe 1 */
}
En general, si la “condición” de un if es
algo que valga 0, se considerará que la condición es
falsa (no se cumple), y si es algo distinto de cero, se
considerará que la condición es verdadera (sí se
cumple). Eso nos permite hacer cosas como ésta:
/*---------------------------*/
/* Ejemplo en C nº 21: */
/* C021.C */
/* */
/* Condiciones con if (8) */
/*---------------------------*/
#include <stdio.h>
int numero;
main() {
printf("Escribe un número: ");
scanf("%d", &numero);
if (numero!=0) /* Comparación normal */
printf("El número no es cero.\n");
if (numero) /* Comparación “con truco” */
printf("Y sigue sin ser cero.\n");
}
En este ejemplo, la expresión “if (numero)”
se fijará en el valor de la variable “numero”. Si es
distinto de cero, se considerará que la condición es
correcta, y se ejecutará la sentencia correspondiente (el
“printf”). En cambio, si la variable “numero”
vale 0, es considera que la condición es falsa, y no se sigue
analizando.
En general, es preferible evitar este tipo de construcciones.
Resulta mucho más legible algo como “if (numero!=0)”
que “if(numero)”.
3.1.7. El peligro de la asignación en un “if”
Cuidado con el operador de igualdad: hay que recordar que el
formato es if (a==b) ... Si no nos damos cuenta y escribimos if (a=b)
estamos asignando a "a" el valor de "b".
Afortunadamente, la mayoría de los compiladores nos avisan con un mensaje parecido a "Possibly incorrect assignment" (que podríamos traducir por "posiblemente esta asignación es incorrecta") o "Possibly unintended assignment" (algo así como "es posible que no se pretendiese hacer esta asignación"). Aun así, sólo es un aviso, la compilación prosigue, y se genera un ejecutable, que puede que se comporte incorrectamente. Vamos a verlo con un ejemplo:
/*---------------------------*/
/* Ejemplo en C nº 22: */
/* C022.C */
/* */
/* Condiciones con if (9) */
/* Comportamiento */
/* incorrecto */
/*---------------------------*/
#include <stdio.h>
int numero;
main()
{
printf("Escriba un número: ");
scanf("%d", &numero);
if (numero < 0)
printf("El número es negativo.\n");
Fundamentos de programación en C, por Nacho Cabanes
Revisión 0.05 – Página 48
else
if (numero = 0)
printf("El número es cero.\n");
else
printf("El número es positivo.\n");
}
En este caso, si tecleamos un número negativo, se
comprueba la primera condición, se ve que es correcta y se
termina sin problemas. Pero si se teclea cualquier otra cosa (0 o
positivo), la expresión “if (numero=0)” no comprueba
su valor, sino que le asigna un valor 0 (falso), por lo que siempre se
realiza la acción correspondiente a el “caso
contrario” (else): siempre se escribe “El número es
positivo”... ¡aunque hayamos tecleado un 0!
Y si esto es un error, ¿por qué el compilador “avisa” en vez de parar y dar un error “serio”? Pues porque no tiene por qué ser necesariamente un error: podemos hacer
a = b
if (a > 2) ...
o bien
if ((a=b) > 2) ...
Es decir, en la misma orden asignamos el valor y comparamos
(algo parecido a lo que hacíamos con "b = ++a", por ejemplo). En
este caso, la asignación dentro del “if”
sería correcta.
3.1.8. Introducción a los diagramas de flujo
A veces puede resultar difícil ver claro donde usar un “else” o qué instrucciones de las que siguen a un “if” deben ir entre llaves y cuales no. Generalmente la dificultad está en el hecho de intentar teclear directamente un programa en C, en vez de pensar en el problema que se pretende resolver.
Para ayudarnos a centrarnos en el problema, existen notaciones gráficas, como los diagramas de flujo, que nos permiten ver mejor qué se debe hacer y cuando.
En primer lugar, vamos a ver los 4 elementos básicos de un diagrama de flujo, y luego los aplicaremos a un caso concreto.
El inicio o el final del programa se indica dentro de un
círculo. Los procesos internos, como realizar operaciones, se
encuadran en un rectángulo. Las entradas y salidas (escrituras
en pantalla y lecturas de teclado) se indican con un paralelogramo que
tenga su lados superior e inferior horizontales, pero no tenga
verticales los otros dos. Las decisiones se indican dentro de un rombo.
Vamos a aplicarlo al ejemplo de un programa que pida un número al usuario y diga si es positivo, negativo o cero:
El paso de aquí al correspondiente programa en lenguaje
C (el que vimos en el ejemplo 19) debe ser casi inmediato: sabemos como
leer de teclado, como escribir en pantalla, y las decisiones
serán un “if”, que si se cumple ejecutará la
sentencia que aparece en su salida “si” y si no se cumple
(“else”) ejecutará lo que aparezca en su salida
“no”.
Ejercicio propuesto: Crear el diagrama de flujo y la
versión en C de un programa que dé al usuario tres
oportunidades para adivinar un número del 1 al 10.
3.1.9. Operador condicional: ?
En C hay otra forma de asignar un valor según se
dé una condición o no. Es el “operador
condicional” ? : que se usa
condicion ? valor1 : valor2;
y equivale a decir “si se cumple la condición,
toma el valor v1; si no, toma el valor v2”. Un ejemplo de
cómo podríamos usarlo sería
numeroMayor = (a>b) ? a : b;
que, aplicado a un programa sencillo, podría ser
/*---------------------------*/
/* Ejemplo en C nº 23: */
/* C023.C */
/* */
/* El operador condicional */
/*---------------------------*/
#include <stdio.h>
int a, b, mayor;
main()
{
printf("Escriba un número: ");
scanf("%d", &a);
printf("Escriba otro: ");
scanf("%d", &b);
mayor = (a>b) ? a : b;
printf("El mayor de los números es %d.\n", mayor);
}
Un segundo ejemplo, que sume o reste dos números según la opción que se escoja, sería:
/*---------------------------*/
/* Ejemplo en C nº 24: */
/* C024.C */
/* */
/* Operador condicional - 2 */
/*---------------------------*/
#include <stdio.h>
int a, b, resultado;
int operacion;
main()
{
printf("Escriba un número: ");
scanf("%d", &a);
printf("Escriba otro: ");
scanf("%d", &b);
printf("Escriba una operación (1 = resta; otro = suma): ");
scanf("%d", &operacion);
resultado = (operacion == 1) ? a-b : a+b;
printf("El resultado es %d.\n", resultado);
}
Ejercicio propuesto: Crear un programa que use el operador
condicional para mostrar un el valor absoluto de un número de la
siguiente forma: si el número es positivo, se mostrará
tal cual; si es negativo, se mostrará cambiado de signo.
3.1.10. Switch
Si queremos ver varios posibles valores, sería muy pesado tener que hacerlo con muchos "if" seguidos o encadenados. La alternativa es la orden "switch", cuya sintaxis es
switch (expresión)
{
case valor1: sentencia1;
break;
case valor2: sentencia2;
sentencia2b;
break;
...
case valorN: sentenciaN;
break;
default:
otraSentencia;
};
Es decir, se escribe tras “switch” la
expresión a analizar, entre paréntesis. Después,
tras varias órdenes “case” se indica cada uno de los
valores posibles. Los pasos (porque pueden ser varios) que se deben dar
si se trata de ese valor se indican a continuación, terminando
con “break”. Si hay que hacer algo en caso de que no se
cumpla ninguna de las condiciones, se detalla tras
“default”.
Vamos a verlo con un ejemplo:
/*---------------------------*/
/* Ejemplo en C nº 25: */
/* C025.C */
/* */
/* La orden “switch” */
/*---------------------------*/
#include <stdio.h>
char tecla;
main()
{
printf("Pulse una tecla y luego Intro: ");
scanf("%c", &tecla);
switch (tecla)
{
case ' ': printf("Espacio.\n");
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0': printf("Dígito.\n");
break;
default: printf("Ni espacio ni dígito.\n");
}
}
Ejercicio propuesto: Crear un programa que lea una letra
tecleada por el usuario y diga si se trata de una vocal, una cifra
numérica o una consonante.
Ejercicio propuesto: Crear un programa que lea una letra tecleada por
el usuario y diga si se trata de un signo de puntuación, una
cifra numérica o algún otro carácter.
3.2. Estructuras repetitivas
Hemos visto cómo comprobar condiciones, pero no cómo hacer que una cierta parte de un
programa se repita un cierto número de veces o mientras se cumpla una condición (lo que
llamaremos un “bucle”). En C tenemos varias formas de conseguirlo.
3.2.1. While
Si queremos hacer que una sección de nuestro programa
se repita mientras se cumpla una cierta condición, usaremos la
orden “while”. Esta orden tiene dos formatos distintos,
según comprobemos la condición al principio o al final.
En el primer caso, su sintaxis es
while (condición)
sentencia;
Es decir, la sentencia se repetirá mientras la
condición se cierta. Si la condición es falsa ya desde un
principio, la sentencia no se ejecuta nunca. Si queremos que se repita
más de una sentencia, basta agruparlas entre { y }.
Un ejemplo que nos diga si cada número que tecleemos es positivo o negativo, y que pare cuando tecleemos el número 0, podría ser:
/*---------------------------*/
/* Ejemplo en C nº 26: */
/* C026.C */
/* */
/* La orden “while” */
/*---------------------------*/
#include <stdio.h>
int numero;
main()
{
printf("Teclea un número (0 para salir): ");
scanf("%d", &numero);
while (numero!=0)
{
if (numero > 0) printf("Es positivo\n");
else printf("Es negativo\n");
printf("Teclea otro número (0 para salir): ");
scanf("%d", &numero);
}
}
En este ejemplo, si se introduce 0 la primera vez, la
condición es falsa y ni siquiera se entra al bloque del "while",
terminando el programa inmediatamente.
Nota: si recordamos que una condición falsa se evalúa como el valor 0 y una condición verdadera como una valor distinto de cero, veremos que ese “while (numero != 0)” se podría abreviar como “while (numero)”.
Ejercicio propuesto: Crear un programa calcule cuantas cifras tiene un número entero positivo (pista: se puede hacer dividiendo varias veces entre 10).
3.2.2. Do ... While
Este es el otro formato que puede tener la orden
“while”: la condición se comprueba al final. El
punto en que comienza a repetirse se indica con la orden
“do”, así:
do
sentencia;
while (condición)
Al igual que en el caso anterior, si queremos que se repitan
varias órdenes (es lo habitual), deberemos encerrarlas entre
llaves.
Como ejemplo, vamos a ver cómo sería el típico programa que nos pide una clave de acceso y nos nos deja entrar hasta que tecleemos la clave correcto. Eso sí, como todavía no sabemos manejar cadenas de texto, la clave será un número:
/*---------------------------*/
/* Ejemplo en C nº 27: */
/* C027.C */
/* */
/* La orden “do..while” */
/*---------------------------*/
#include <stdio.h>
int valida = 711;
int clave;
main()
{
do
{
printf("Introduzca su clave numérica: ");
scanf("%d", &clave);
if (clave != valida) printf("No válida!\n");
}
while (clave != valida);
printf("Aceptada.\n");
}
En este caso, se comprueba la condición al final, de
modo que se nos preguntará la clave al menos una vez. Mientras
que la respuesta que demos no sea la correcta, se nos vuelve a
preguntar. Finalmente, cuando tecleamos la clave correcta, el ordenador
escribe "Aceptada" y
termina el programa.
Ejercicio propuesto: Crear un programa que pida números positivos al usuario, y vaya calculando la suma de todos ellos (terminará cuando se teclea un número negativo o cero).
3.2.3. For
Ésta es la orden que usaremos habitualmente para crear
partes del programa que se repitan un cierto número de veces. El
formato de “for” es
for (valorInic; CondicRepetic; Incremento)
Sentencia;
Así, para contar del 1 al 10, tendríamos 1 como valor inicial, <=10 como condición de repetición, y el incremento sería de 1 en 1. Por tanto, el programa quedaría:
/*---------------------------*/
/* Ejemplo en C nº 28: */
/* C028.C */
/* */
/* Uso básico de “for” */
/*---------------------------*/
#include <stdio.h>
int contador;
main()
{
for (contador=1; contador<=10; contador++)
printf("%d ", contador);
}
Recordemos que "contador++" es una forma abreviada de escribir
"contador=contador+1", de modo que en este ejemplo aumentamos la
variable de uno en uno.
Ejercicio propuesto: Crear un programa que muestre los primeros ocho números pares.
En un “for”, realmente, la parte que hemos llamado
“Incremento” no tiene por qué incrementar la
variable, aunque ése es su uso más habitual. Es
simplemente una orden que se ejecuta cuando se termine la
“Sentencia” y antes de volver a comprobar si todavía
se cumple la
condición de repetición. Por eso, si escribimos la siguiente línea:
for (contador=1; contador<=10; )
la variable "contador" no se incrementa nunca, por lo que
nunca se cumplirá la condición de salida: nos quedamos
encerrados dando vueltas dentro de la orden que siga al "for".
Un caso todavía más exagerado de algo a lo que se entra y de lo que no se sale sería la siguiente orden:
for ( ; ; )
Los bucles "for" se pueden anidar (incluir uno dentro de otro), de modo que podríamos escribir las tablas de multiplicar del 1 al 5 con:
/*---------------------------*/
/* Ejemplo en C nº 29: */
/* C029.C */
/* */
/* “for” anidados */
/*---------------------------*/
#include <stdio.h>
int tabla, numero;
main()
{
for (tabla=1; tabla<=5; tabla++)
for (numero=1; numero<=10; numero++)
printf("%d por %d es %d\n", tabla, numero, tabla*numero);
}
En estos ejemplos que hemos visto, después de "for" había un única sentencia. Si queremos que se hagan varias cosas, basta definirlas como un bloque (una sentencia compuesta) encerrándolas entre llaves. Por ejemplo, si queremos mejorar el ejemplo anterior haciendo que deje una línea en blanco entre tabla y tabla, sería:
/*---------------------------*/
/* Ejemplo en C nº 30: */
/* C30.C */
/* */
/* “for” anidados (2) */
/*---------------------------*/
#include <stdio.h>
int tabla, numero;
main()
{
for (tabla=1; tabla<=5; tabla++)
{
for (numero=1; numero<=10; numero++)
printf("%d por %d es %d\n", tabla, numero, tabla*numero);
printf("\n");
}
}
Para "contar" no necesariamente hay que usar números. Por ejemplo, podemos contar con letras así:
/*---------------------------*/
/* Ejemplo en C nº 31: */
/* C031.C */
/* */
/* “for” que usa “char” */
/*---------------------------*/
#include <stdio.h>
char letra;
main()
{
for (letra='a'; letra<='z'; letra++)
printf("%c", letra);
}
En este caso, empezamos en la "a" y terminamos en la "z", aumentando de uno en uno.
Si queremos contar de forma decreciente, o de dos en dos, o como nos interese, basta indicarlo en la condición de finalización del "for" y en la parte que lo incrementa:
/*---------------------------*/
/* Ejemplo en C nº 32: */
/* C032.C */
/* */
/* “for” que descuenta */
/*---------------------------*/
#include <stdio.h>
char letra;
main()
{
for (letra='z'; letra>='a'; letra-=2)
printf("%c", letra);
}
3.3. Sentencia break: termina el bucle
Podemos salir de un bucle “for” antes de tiempo con la orden “break”:
/*---------------------------*/
/* Ejemplo en C nº 32: */
/* C032.C */
/* */
/* “for” que descuenta */
/*---------------------------*/
#include <stdio.h>
int i;
main()
{
for (i=0; i<=10; i++)
{
if (i==5) break;
printf("%d ", i);
}
}
El resultado de este programa es:
0 1 2 3 4
(en cuanto se llega al valor 5, se interrumpe el “for”, por lo que no se alcanza el valor 10).