Download TEMA 3. Sentencias de control. - LSI

Document related concepts

Bucle infinito wikipedia , lookup

Estructuras de control wikipedia , lookup

Sentencia condicional wikipedia , lookup

Bucle do wikipedia , lookup

Bucle while wikipedia , lookup

Transcript
Autor: José C. Riquelme (Universidad de Sevilla)
TEMA 3. Sentencias de control.
1. Introducción.
En principio, las sentencias de un programa se ejecutan secuencialmente, esto es, cada
una a continuación de la anterior empezando por la primera y acabando por la última.
Sin embargo, es fácil encontrar ejemplos de programas cuyo flujo de ejecución no
puede ser secuencial. Pensemos por ejemplo en un menú básico de un cajero
automático:
Menú de opciones:
1. Sacar Dinero
2. Consulta de Saldo
3. Últimos Movimientos
4. Salir
Teclee la opción deseada: _
El funcionamiento habitual de este tipo de programas es el siguiente: el usuario teclea
un número de opción (1, 2, 3 ó 4). Si la opción tecleada no fuera una de estas opciones,
el programa después de advertir del error volvería a solicitar una opción correcta. Si la
opción es válida el programa ejecutará las sentencias correspondientes a la acción
seleccionada y una vez acabadas, lo normal es que el programa pregunte si queremos
realizar otra operación y si es así volverá al menú inicial esperando de nuevo otra
opción.
En esta secuencia de ejecución podemos detectar dos tipos de modificación del flujo
secuencial: bucles y bifurcaciones.
Por un lado, observamos que hay una secuencia de pasos que deben repetirse un numero
determinado de veces: por ejemplo, si la opción tecleada no es correcta hay que solicitar
una que lo sea y también es un bucle cuando termina la primera operación, si el usuario
quiere realizar otra.
Por otro lado, cuando se escoge una determinada operación el programa debe ejecutar
sólo el grupo de sentencias que realizan esa operación, lo que implica que el control del
programa debe dirigirse a unas sentencias, saltándose otras, y que cuando terminen,
debe volver el control al bucle inicial.
El lenguaje C dispone de varias sentencias para modificar el flujo secuencial de la
ejecución: fundamentalmente las sentencias if y switch1 implementan las bifurcaciones,
que como hemos señalado permiten elegir entre dos o más opciones según ciertas
condiciones, y las sentencias for y while2, que permiten ejecutar repetidamente un
conjunto de instrucciones tantas veces como se desee, cambiando o actualizando ciertos
valores.
1
2
También existe el operador condicional o ternario ? que no será estudiado en esta asignatura
También existe una sentencia do-while que no será objeto de estudio en este manual
Tema 3. Sentencias de Control
2. Bifurcaciones
2.1 Sentencia if. Esta sentencia de control permite ejecutar o no una sentencia simple o
compuesta según se cumpla o no una determinada condición. Esta sentencia tiene la
siguiente forma general:
if (condición) {
sentencia-1;
...
sentencia-n;
} [else {
sentencia-n+1;
...
sentencia-m;
}]
El flujo del control de la sentencia if es el de la siguiente figura:
¿Cómo funciona la sentencia if? En primer lugar se evalúa la expresión booleana que
figura como condición. Esta evaluación debe dar un valor de true (como hemos dicho
antes significa un valor distinto de cero) o un valor de false (o igual a cero). Si el valor
de condición es cierta entonces sólo se ejecutarían las sentencias de la 1 a la n. Si por el
contrario, condición es falsa, se ejecutarán solo las sentencias de la n+1 a la m.
Algunas cuestiones sintácticas:
1. La condición debe estar siempre entre paréntesis.
2. La sección else con el bloque de las sentencias correspondiente es optativa, es decir,
no es obligatoria.
3. Las llaves que marcan los bloques son obligatorias si hay más de una sentencia,
aunque se recomienda ponerlas siempre, aun cuando sólo haya una sentencia.
4. Como se puede observar no hay punto y coma después de la condición del if
Autor: José C. Riquelme (Universidad de Sevilla)
Ejemplo 1:
if (n%2==0){
printf(“El número %d es par”,n);
}
else {
printf(“El número %d es impar\n”,n);
}
Ejemplo 2:
if (n>0){
m=factorial(n);
printf(“El factorial de %d es %d\n”,n,m);
}
Ejemplo 3:
if (x>0 && x<1){
y=sin(x);
printf(“El seno de %f es %lf\n”,x,y);
}
else {
y=cos(x);
printf(“El coseno de %f es %lf\n”,x,y);
}
La sentencia if-else pueden encadenarse haciendo que se evalúe un conjunto de
condiciones y se ejecute una sola de las opciones entre varias. Por ejemplo, la
implementación del menú del cajero automático que hemos puesto al principio del tema
sería de la siguiente manera:
Ejemplo 4 (if-else encadenados):
if (tecla==1){
ejecuta_funcion_sacar_dinero();
}
else if (tecla==2){
ejecuta_funcion_consulta_de_saldo();
}
else if (tecla==3){
ejecuta_funcion_ultimos_movimientos();
}
else if (tecla==4){
ejecuta_funcion_salir();
} else {
printf(“Opción no válida\n”);
}
Otra posibilidad es que en los bloques de sentencias correspondientes al if o al else haya
otros if. A esta estructura se le llama if-else anidados. Por ejemplo, para detectar el
cuadrante en el que se encuentran las coordenadas x e y de un punto dado:
Tema 3. Sentencias de Control
Ejemplo 5 (if-else anidados):
printf(“Escriba la abscisa\n”);
scanf(“%f”,&x);
printf(“Escriba la ordenada\n”);
scanf(“%f”,&y);
if (x>=0){
if (y>=0){
printf (“El punto (%f,&f) está en el primer cuadrante\n”);
}
else {
printf (“El punto (%f,&f) está en el cuarto cuadrante\n”);
}
else {
if (y>=0){
printf (“El punto (%f,&f) está en el segundo cuadrante\n”);
}
else {
printf (“El punto (%f,&f) está en el tercer cuadrante\n”);
}
}
/* otra posible versión del mismo problema con if-else encadenados*/
/* menos adecuada que la anterior
*/
if (x>=0 && y>=0){
printf (“El punto (%f,&f) está en el primer cuadrante\n”);
}
else if (x>=0 && y<0){
printf (“El punto (%f,&f) está en el cuarto cuadrante\n”);
}
else if (y>=0 && x<0){
printf (“El punto (%f,&f) está en el segundo
cuadrante\n”);
}
else if (y<0 && x<0){
printf (“El punto (%f,&f) está en el tercer
cuadrante\n”);
}
}
Las sentencias con if anidados pueden presentar problemas de legibilidad sino
empleamos las llaves de bloque convenientemente. Por ejemplo:
if (a >= b)
if (b != 0.0)
c = a/b;
else
c = 0.0;
Esta sentencia puede parecer ambigua porque no se han puesto llaves, y podrían surgir
dudas respecto a si el else corresponde al primer o al segundo if. La clave no está en la
indentación (recuerde que el C se “saltará” al compilar todos los espacios en blanco y
tabuladores) sino en la regla de que un else siempre corresponde al if anterior. Por lo
tanto, la sentencia anterior es equivalente a esta otra, en la que las llaves despejan las
dudas que pudiera haber sobre a qué if corresponde el else:
Autor: José C. Riquelme (Universidad de Sevilla)
if (a >= b){
if (b != 0.0){
c = a/b;
}
else{
c = 0.0;
}
}
// cierra segundo if
//abre else de segundo if
// cierra else
// cierra primer if que no tiene else
Si se quiere modificar esta regla entonces es absolutamente obligatorio usar llaves:
if (a >= b){
if (b != 0.0){
c = a/b;
}
}
else{
c = 0.0;
}
// cierra segundo if
// cierra primer if
//abre else de primer if
// cierra else
Estos problemas no deben darse en un buen programador porque siempre pone todas las
llaves, aunque no hagan falta.
2.2 Sentencia switch. La sentencia switch desarrolla una función similar a la sentencia
if -else múltiple que hemos visto en el ejemplo 4 anterior aunque con algunas
limitaciones. La forma general de la sentencia switch es la siguiente:
switch (expresión) {
case expresion_cte_1:
bloque_sentencia_1;
[break;]
case expresion_cte_2:
bloque_sentencia_2;
[break;]
...
case expresion_cte_n:
bloque_sentencia_n;
[break;]
[default:
bloque_sentencia_n+1;
]
}
La opción default y las sentencias break son optativas (por eso figuran entre corchetes).
¿Cómo funciona la sentencia switch? Supongamos en primer lugar que no hay ninguna
sentencia break, En ese caso, se evalúa expresión que debe devolver obligatoriamente
un valor de tipo int o char. Si este valor coincide con el valor constante
expresion_cte_1, se ejecutan todos los bloques de sentencias desde la 1 hasta la n. Si el
resultado coincide con el valor constante expresion_cte_2, se ejecutan los bloques desde
el 2 hasta el n. En general, si el valor de expresión es igual a expresión_cte_i, se
ejecutan todos los bloques desde i hasta n. Si ninguna expresion_cte_i coincide con el
valor de expresión se ejecutará, si existe, el bloque _sentencia_n+1 que está a
continuación de default.
Tema 3. Sentencias de Control
Si se desea ejecutar únicamente un bloque_sentencia_i basta poner una sentencia break
a continuación. El efecto de la sentencia break es dar por terminada la ejecución de la
sentencia switch. Existe también posibilidad de ejecutar el mismo bloque_sentencia_i
para varios valores del resultado de expresión. Para ello se deben poner varios case
expresion_cte : seguidos.
Ejemplo 6:
switch (tecla){
case 1:
ejecuta_funcion_sacar_dinero();
break;
case 2:
ejecuta_funcion_consulta_saldo();
break;
case 3:
ejecuta_funcion_ultimos_movimientos();
break;
case 4:
ejecuta_funcion_salir();
break;
default:
printf(“opción no válida\n”);
}
Ejemplo 7:
switch(caracter){
case 'a':
printf(“1”);
case 'b':
printf(“2”);
break;
case 'c':
printf(“3”);
case 'd': case 'e':
printf(“4”);
}
3. Bucles
Los bucles son estructuras de control para que un conjunto de sentencias puedan
ejecutarse repetidas veces. Básicamente hay dos tipos de bucles, aquellos que se repiten
un número determinado de veces, número que es conocido antes de empezar el bucle; o
aquellos bucles que se ejecutan un número indeterminado de veces, y cuya terminación
viene dada por una determinada condición que depende de la ejecución del programa.
Un ejemplo del primer caso, sería el código para leer un número n positivo y después
calcular la suma de n números que se leerían desde teclado. Un ejemplo del segundo
caso sería calcular la suma de una relación de números que se leen por teclado hasta que
Autor: José C. Riquelme (Universidad de Sevilla)
se introduzca el valor -1. La sentencia habitual para el primer caso es la estructura for y
para el segundo caso la estructura while.
3.1 Sentencia for. La estructura for tiene la siguiente sintaxis3:
for (inicialización ; condición ; actualización) {
sentencia-1;
sentencia-2;
...
sentencia-n;
}
Donde inicialización y actualización son sentencias y condición
es una expresión booleana.
Su significado y flujo de control lo podemos ver en el siguiente gráfico:
¿Cómo funciona el for? Cuando el control de un programa llega a una sentencia for, lo
primero que se ejecuta es la inicialización. A continuación se evalúa condición, si es
cierta (valor distinto de cero), se ejecuta el bloque de sentencias de 1 a n. Una vez
terminado el bloque se ejecuta la sentencia de actualización, antes de volver a evaluar la
condición, si ésta fuera cierta de nuevo se ejecutaría el bloque y la actualización, y así
repetidas veces hasta que la condición fuera falsa y abandonáramos el bucle.
Algunos detalles de la sintaxis:
1. Como se puede observar las tres partes de la sentencia for están entre paréntesis
y separadas por ; pero no se escribe ; después del paréntesis de cerrar.
3
Aunque el C permite más de una sentencia tanto en la inicialización como en la actualización separadas
por comas, en esta asignatura sólo vamos a trabajar con for que tengan una única sentencia.
Tema 3. Sentencias de Control
2. Si el bloque de sentencias a ejecutar es de solo una, no es obligatorio poner
llaves, aunque como señalamos antes es muy recomendable por legibilidad.
3. Si después de la inicialización, la condición fuera falsa, las sentencias del bloque
no se ejecutarían ni una sola vez.
La sentencia for del C es muy versátil y permite numerosas posibilidades. Sin embargo,
una buena costumbre de programación es hacer el código lo más legible y fácil de
mantener posible. Esto nos lleva a limitar el uso de la sentencia for en este manual a
casos muy concretos. Estos casos serán aquellos en los que ejecutamos un conjunto de
sentencias un número fijo de veces. Por ejemplo:
Ejemplo 8 (hallar la suma de n números):
printf(“Introduzca cuántos números va a sumar) ;
scanf(“%d”,&n);
suma=0.0;
for (i=0;i<n;i++){
scanf(“%f”,&x);
suma = suma + x; // suma +=x;
}
printf(“la suma de los %d números leídos vale %f”,n,suma);
Como se puede observar la sintaxis se reduce a una inicialización del contador (i=0),
una actualización (i++) y la condición se evalúa a falsa y, por tanto, el bucle termina
cuando se han leído n números (desde 0 hasta n-1). De manera más natural se podría
haber puesto el bucle desde 1 hasta n incluido (i<=n). La razón para empezar en 0 se
verá en el siguiente tema.
3.2 Sentencia while. La sentencia while tiene la siguiente sintaxis:
// aquí iría la inicialización
while (condición) {
sentencia-1;
sentencia-2;
...
sentencia-n; //alguna debe ser actualización
}
Su diagrama de flujo es:
Autor: José C. Riquelme (Universidad de Sevilla)
¿Cómo funciona la sentencia while? Primero se evalua la condición que debe ser una
expresión booleana, si es cierta, se ejecutan las sentencias de la 1 a la n. Una vez
acabadas se vuelve a evaluar la condición, si sigue siendo cierta se ejecuta de nuevo el
bloque de sentencias, y así sucesivamente hasta que la condición sea falsa.
Algunas anotaciones sobre la sentencia while:
1. Al igual que en la sentencia if, la condición debe ir entre paréntesis.
2. Normalmente una estructura while tiene más de una sentencia asociada, por eso las
llaves son obligatorias.
3. Entre las sentencias del bloque debe haber alguna que modifique la condición, para
que en algún momento, ésta sea falsa.
4. El bucle while no tiene expresamente una inicialización, así que es tarea del
programador inicializar convenientemente las variables que intervienen en el bucle.
5. Si la primera vez que se evalúa la condición fuera falsa, las sentencias del bloque no
se ejecutarían ni una sola vez.
Por ejemplo si quisiéramos hallar la suma de números leídos desde teclado mientras
sean positivos, el bucle sería
Ejemplo 9:
suma=0.0;
printf(“Introduzca un número positivo, 0 para finalizar\n”) ;
scanf(“%f”,&x);
while (x>0){
suma = suma + x;
printf(“Introduzca un número positivo,0 para finalizar\n”);
scanf(“%f”,&x);
}
printf(“la suma de los números leídos vale %f”,suma);
Tema 3. Sentencias de Control
Nótese el recurso de hacer la primera lectura fuera del bucle, si el primer número que
leemos ya es 0, no se lee ninguno más y la suma daría cero. Si el número es positivo, se
añadiría a suma, leyendo el segundo valor que solo se sumaría si es positivo, y así
sucesivamente hasta que se lea un valor no positivo.
Una variante más compleja de este problema sería si nos pidieran leer números
positivos pero no más de un determinado número. Esto es, por ejemplo lea números
positivos pero no más de 10 o dicho de otra manera, pare de leer si lee un número
negativo o ya ha leído 10. Para ello, es necesario introducir un contador y añadir una
condición con él en el while.
Ejemplo 10:
suma=0.0;
printf(“Introduzca un número positivo, 0 para finalizar\n”) ;
scanf(“%f”,&x);
contador=1;
while (x>0 && contador < 10){
suma = suma + x;
printf(“Introduzca un número positivo,0 para finalizar\n”);
scanf(“%f”,&x);
contador++;
}
printf(“la suma de los números leídos vale %f”,suma);
4 Sentencias break y continue4.
Las sentencias break y continue son reminiscencias de otros lenguajes y sobre todo de
otros estilos de programación. Actualmente son muy poco usadas y, en cualquier caso,
deben ser de localizadas en sitios muy concretos donde ayuden a la legibilidad del
código o a su eficiencia, sin necesidad de complicar la condición de los bucles. Ambas
sentencias se pueden incluir entre el bloque de sentencias de un bucle, normalmente
como parte de una sentencia if.
La sentencia break interrumpe la ejecución del bucle donde se ha incluido, haciendo al
programa salir de él aunque la condición correspondiente a ese bucle sea verdadera.
La sentencia continue hace que el programa comience con la siguiente iteración del
bucle donde se halla, aunque no haya llegado al final de las sentencias del bloque.
Ejemplo 11 (bucle con break):
Suponga que queremos saber si entre una lista de 100 números enteros que se van a leer
desde teclado existe alguno entre 25 y 30 ambos inclusive.
4
Por razones históricas existe en C incluso una sentencia goto. Puede buscar documentación sobre por
qué esta sentencia ha desaparecido de los lenguajes modernos.
Autor: José C. Riquelme (Universidad de Sevilla)
existe=0;
for (i=0;i<100;i++){
scanf(“%d”,&n);
if (n>=25 && n<=30){
existe=1;
break;
}
}
if (existe) // equivalente a if (existe==1)
printf(“entre los números leídos había uno en [25,30]\n”);
else
printf(“entre los números leídos no había en [25,30]\n”);
Las sentencias break presentan problemas de legibilidad del programa sobre todo si se
abusa de ellas. Algunos autores predican que no deben usarse porque no “estructuran”
bien el código. El ejemplo anterior sin necesidad de break sería el siguiente. Juzgue el
lector cómo le resulta más fácil entenderlo.
Ejemplo 12 (bucle sin necesidad de break):
existe=0;
i=0;
while (i<100 && ! existe){ // equivalente a (i<100 && existe!=0)
scanf(“%d”,&n);
if (n>=25 && n<=30){
existe=1;
}
i++;
}
if (existe)
printf(“entre los números leídos había uno en [25,30]\n”);
else
printf(“entre los números leídos no había en [25,30]\n”);
5. Ejercicios.
1. Todos los ejemplos de este tema están resueltos “en el aire”, esto es, son
sentencias sueltas que no están incluidas en ningún programa en C. Por ejemplo,
faltan las declaraciones de las variables. Vuelva a escribir todos los ejemplos de
este tema, en dos versiones: la primera escriba el código en una función main; la
segunda escribiendo la solución del problema mediante una función que debe ser
invocada desde el programa principal. Recuerde que esta segunda forma es más
correcta que la primera y reflexione por qué.
Para todos los problemas siguientes, escriba un programa principal que invoque a la
función con distintos parámetros.
2. Escriba una función que reciba dos números enteros y devuelva si el primero es
múltiplo del segundo.
3. Escriba una función tal que dados tres números reales los imprima en orden de
mayor a menor. Tenga en cuenta el siguiente esquema:
Tema 3. Sentencias de Control
si: abc
si: ¿b>c?
si: ¿a>c?
no: acb
no: cab
¿a>b?
si: bac
no: ¿a>c?
si: bca
no: ¿b>c?
no: cba
4. Escriba una función tal que dados tres números reales devuelva el mayor.
5. Escriba una función que reciba un entero y devuelva si representa un año
bisiesto o no, según la regla:
Un año es bisiesto si es divisible por 400, o bien si es divisible por 4 pero no por 100
Por ejemplo, el año 2000 es bisiesto (es divisible por 400), el año 1992 es
bisiesto (es divisible por 4 y no por 100), y el año 2100 no es bisiesto (es
divisible por 4 y también por 100 y no por 400).
6. Escriba una función que reciba tres números reales representando las longitudes
de tres segmentos y devuelva si es posible formar con ellos un triángulo.
7. Escriba una función tal que dado un carácter, si está en mayúsculas devuelva el
mismo carácter en minúsculas y viceversa.
8. Escriba una función que devuelva el signo de la quiniela, dado el resultado de un
partido.
9. Escriba una función que reciba un número n como argumento y escriba la tabla
de multiplicar de n (desde 1 hasta 10). Escriba un programa principal que lea un
número entero positivo desde teclado e imprima la tabla de ese número.
10. Supongamos que en el programa anterior si el número leído no es positivo, debe
imprimir un mensaje y volver a leer hasta que sea positivo.
11. ¿Cómo haría usando el problema 2 para escribir en pantalla todas las tablas de
multiplicar del 1 al 10?
12. Si definimos int i=1,j=5,k=10; Entonces la salida de la siguiente sentencia es:
while(i<k){
if ((i*j)%k==0){
printf("%d",i);
}
i++;
}
Autor: José C. Riquelme (Universidad de Sevilla)
13. Si definimos int i=1,j=1,k=10; Entonces la salida de la siguiente sentencia
es:
for(i=0;i<k;i++){
if((i+j)%3==0){
printf("%d",i);
j++;
}
}
14. Escriba una función que averigüe si un número es cuadrado perfecto o no.
15. Escriba una función que reciba un número entero positivo n y devuelva 1 si n es
primo ó 0 si no lo es. Para ello debe comprobar los restos de las divisiones de n
entre m para m tomando valores entre 2 y n-1. Si algún resto da 0 es que el
número n es divisible por m y por tanto, no es primo.