Introducción al lenguaje C. Sentencias de Control

Elementos de un programa C. Tipos básicos de datos. E/S básica. Sentencias de control. Funciones. Asignación dinámica de memoria. Ficheros. Ficheros indexados: la interfase Btrieve. Compilación y enlazado. Biblioteca de funciones de Turbo C

  • Enviado por: Juan
  • Idioma: castellano
  • País: España España
  • 18 páginas
publicidad
publicidad

5

Sentencias de control

La estructura if

La estructura if adopta una de las dos formas siguientes:

if (expresión) sentencia;

o bien

if (expresión) sentencia;

else sentencia;

en donde expresión es una sentencia que se evalúa como verdadera (devuelve un valor no nulo) o falsa (devuelve cero). La palabra sentencia puede ser una sentencia simple terminada con un punto y coma, o un grupo de sentencias encerradas entre llaves {}. Algunos ejemplos válidos de sentencias if son:

  • if (x > 0) puts ("POSITIVO");

  • if (x) puts ("Verdadero");

    • else puts ("Falso");

  • if (c >= 'a' && c <= 'z') {

    • puts ("La variable c almacena un carácter alfabético");

    • puts ("El carácter es una letra minúscula");

    • }

  • if (num <= 40000) {

    • printf ("\nOctal: %o", num);

    • printf ("\nHexadecimal: %X", num); }

    • else {

    • puts ("El número es mas grande que 40000");

    • printf ("Su valor es %u", num);

    • }

Las estructuras if pueden anidarse sin más que tomar un mínimo de precauciones. En las sentencias

if (x)

if (y) puts ("1");

else puts ("2");

el else está asociado a if (y). C siempre asocia los else al if más cercano que no tenga ya un else. Para que en la sentencia anterior el else se asocie a if (x), hay que colocar adecuadamente las llaves {}.

if (x) {

if (y) puts ("1"); }

else puts ("2");

En C también se dispone de la estructura

if (condición1) sentencia1;

else if (condición2) sentencia2;

else if (condición3) sentencia3;

...

else sentenciaN;

que va evaluando, sucesivamente, condición1, condición2, ... Cuando encuentra una cierta, se ejecuta la sentencia correspondiente y se finaliza el if. Si ninguna es cierta se ejecuta la sentencia que acompaña al else (que no es obligatorio). Como siempre, sentencia1, sentencia2, ..., pueden ser una sola sentencia o un grupo de ellas, en cuyo caso se encierran entre llaves {}.

El siguiente programa usa esta estructura para determinar si un número es positivo, negativo o cero.

#include <stdio.h>

#include <conio.h>

void main ()

{

int n;

clrscr ();

printf ("Teclee un número entero: ");

scanf ("%d", &n);

if (n > 0) puts ("Positivo");

else if (n < 0) puts ("Negaivo");

else puts ("Cero");

}

La estructura switch

La estructura switch inspecciona una variable y la va comparando con una lista de constantes. Cuando encuentra una coincidencia, se ejecuta la sentencia o grupo de sentencias asociado. La forma de switch es

switch (variable) {

case cte1: sentencia;

break;

case cte2: sentencia;

break;

...

...

default: sentencia;

}

donde variable es una variable o cualquier expresión que devuelva un valor. La sentencia switch compara la variable con cte1, cte2, ..., y si encuentra una coincidencia, ejecuta la sentencia correspondiente. Por sentencia debemos entender tanto una sentencia simple como un grupo de sentencias (que, en este caso, no se encierran entre llaves). Si no se encuentra ninguna coincidencia se ejecuta la sección default (que no es obligatoria).

El siguiente segmento de programa muestra cómo se utiliza la sentencia switch.

char c;

...

...

c = getche ();

switch (c) {

case 'a': funcion_a ();

break;

case 'b': funcion_b ();

break;

default: puts ("No se ha pulsado ni a ni b");

}

Fijémonos que la sentencia switch busca coincidencias exactas, por lo que no es una alternativa a programas como el de la página anterior, ya que NO ESTÁ PERMITIDO imponer condiciones de desigualdad. No es correcto, por tanto

int n;

...

...

switch (n) {

case > 0: puts ("Positivo");

break;

case < 0: puts ("Negativo");

break;

default: puts ("Cero");

}

La sentencia break es opcional. Cuando se encuentra, provoca la salida de switch. En caso contrario continua la siguiente secuencia case o default aunque no se cumpla la condición. Para aclarar esto, tomemos el siguiente ejemplo:

int c;

...

...

scanf ("%d", &c);

switch (c) {

case 1:

case 2: Funcion2 ();

case 3: Funcion3 ();

break;

case 4: Funcion4_1 ();

Funcion4_2 ();

break;

case 5: Funcion_5 ();

default: FuncionX ();

}

La siguiente tabla indica qué función se ejecuta dependiendo del valor de c.

Si se pulsa

Se ejecuta

1

Funcion2() y Funcion3()

2

Funcion2() y Funcion3()

3

Funcion3()

4

Funcion4_1() y Funcion4_2()

5

Funcion5() y FuncionX()

cualquier otra cosa

FuncionX()

La sentencia default es opcional. Cuando no está no se ejecuta ninguna acción al fallar todas las coincidencias. Simplemente se abandona el switch sin hacer nada. Si hay sentencia default, el bloque de sentencias asociado se ejecuta cuando fallan todas las comparaciones o no hay un break anterior que lo impida. Las sentencias switch pueden anidarse, con lo que se permiten estructuras del tipo:

switch (m) {

case 1: Funcion1 ();

break;

case 2: switch (n) {

case 21: Funcion21 ();

break;

default: switch (p) {

case 31: Funcion31 ();

break;

case 31: Funcion32 ();

}

}

break;

default: FuncionX ();

}

Bucles

Existen en C tres tipos de bucles: for, while y do/while.

Bucles for

El bucle for es muy potente y flexible. Además de permitir las mismas operaciones que cualquier for de otros lenguajes, tiene características que lo diferencian claramente de ellos. En su formato tradicional este bucle tiene la forma

for (inicialización; condición; incremento) cuerpo_del_bucle;

Vemos que for tiene tres secciones: inicialización, en dónde se da un valor inicial a una variable de control del bucle; condición, que es una expresión que devuelve un valor verdadero o falso, y hace que el bucle se repita mientras sea cierta; e incremento, en dónde se determina la cuantía del incremento o decremento de la variable de control. Las tres secciones están separadas por punto y coma. El cuerpo del bucle puede estar formado por una o por varias sentencias. En este último caso deben encerrarse entre llaves {}. El flujo de sentencias en este bucle es el siguiente:

inicialización

FALSA

condición

VERDADERA

cuerpo_del_bucle

incremento

Fijémonos que el for se sigue ejecutando MIENTRAS la condición sea verdadera.

Veamos un par de ejemplos. En la siguiente secuencia se muestran en pantalla los números del 1 al 10 y sus cuadrados.

register int i;

...

...

for (i = 1; i <= 10; i++) {

printf ("\nValor de i: %d", i);

printf ("\nValor de i2: %d", i * i);

}

En esta, se muestran en pantalla las letras mayúsculas de la A a la Z.

char letra;

...

...

for (letra = 'A'; letra <= 'Z'; letra++) printf ("\n%c", letra);

Puede ponerse un incremento/decremento diferente de 1. El siguiente ejemplo muestra en pantalla los números pares comprendidos entre 1 y 100, descendentemente:

register int i;

...

...

for (i = 100; i >= 1; i = i - 2) printf ("\n%d", i);

Estudiaremos ahora algunas formas de for que se apartan del uso tradicional.

Es posible tener más de una variable de control del bucle: En el bucle for las secciones de inicialización e incremento pueden tener, a su vez, subsecciones, en cuyo caso van separadas por el operador secuencial (,). Un ejemplo es

register int i, j;

...

...

for (i = 0, j = 1; i + j < N; i++, j++) printf ("\n%d", i + j);

que visualiza los N primeros números impares. No debe confundirse esta sentencia con un anidamiento de bucles for. Un anidamiento tiene el siguiente aspecto:

register int i, j;

...

...

for (i = 0; i <= 100; i++) {

...

for (j = 0; j <= 100; j++) {

cuerpo_del_bucle;

}

...

}

La condición de salida del bucle no tiene por qué referirse a la variable de control: Esto queda ilustrado en el siguiente ejemplo:

char a;

register int i;

...

...

for (i = 1; a != 's'; i++) {

printf ("\n%d", i);

a = getch ();

}

En este ejemplo se van mostrando en pantalla los números 1, 2, ... hasta que se teclee el carácter s.

El bucle for puede no tener cuerpo: Esta característica permite crear retardos en un programa. El bucle

register int i;

...

...

for (i = 1; i <= 100; i++);

provoca un retardo de 100 ciclos.

El bucle for puede tener vacía cualquier sección: En un bucle for puede faltar una, dos o las tres secciones. Por ejemplo, es correcto escribir

register int i;

...

...

for (i = 0; i != 10; ) { /* Falta la 3ª sección (incremento) */

scanf ("%d", &i);

printf ("\n%d", i);

}

que va mostrando en pantalla los valores que se tecleen, finalizando al teclear el número 10 (que también se visualiza).

También es correcto un bucle como

for ( ; ; ) {

cuerpo_del_bucle;

}

que es un bucle sin fin. Estudiaremos más adelante, en este mismo capítulo, cómo abandonar un bucle infinito.

Cualquier expresión válida en C puede estar en cualquier sección de un bucle for: La forma del bucle for no tiene que ajustarse necesariamente a la mostrada en la página 67. En realidad la forma correcta es:

for (expresión1; expresión2; expresión3) cuerpo del bucle;

siendo expresiónN cualquier expresión válida C. Podemos decir que, en general, el flujo de sentencias de un bucle for es:

expresión1

FALSA

expresión2

VERDADERA

cuerpo_del_bucle

expresión3

Aclararemos esto con el programa siguiente:

#include <stdio.h>

void main ()

{

int t;

for (mensaje (); t = lee_numero (); cuadrado (t));

}

mensaje ()

{

printf ("\nTeclee un número (0 finaliza): ");

}

lee_numero ()

{

int n;

scanf ("%d", &n);

return n;

}

cuadrado (int x)

{

printf ("\nEl cuadrado es %d", x * x);

}

Vamos a fijarnos en el bucle for de la función main() y explicarlo mediante el diagrama de flujo de la página anterior.

1. Se ejecuta la función mensaje() que muestra la cadena "Teclee un número (0 finaliza): ". (IMPORTANTE: Esta función sólo se ejecuta esta vez).

2. Se evalúa la expresión t = lee_numero(), es decir, lee_numero() captura un número entero del teclado y lo devuelve almacenándolo en t.

2.1 Si la expresión t = lee_numero () devuelve FALSO, es decir, si se ha tecleado 0, finaliza el bucle.

2.2 En caso contrario continúa el bucle.

3. Se ejecuta el cuerpo del bucle. En este caso, dado que for finaliza con punto y coma, no hay cuerpo del bucle.

4. Se ejecuta la función cuadrado(t) que visualiza el cuadrado del número tecleado.

5. Se vuelve al paso 2.

El bucle while

Tiene la forma

while (expresión) cuerpo_del_bucle;

siendo expresión cualquier expresión C válida. El cuerpo_del_bucle, puede estar formado por una sentencia sencilla o por un bloque de sentencias, en cuyo caso, se encierran entre llaves {}. El flujo de sentencias es

FALSA

expresión

VERDADERA

cuerpo_del_bucle

Por lo tanto, en el bucle while el cuerpo_del_bucle se repite mientras expresión se evalúe como cierta. Veamos algunos ejemplos.

char c;

...

...

while (c != 's' && c != 'n') c = getche ();

En esta sentencia se solicita un carácter del teclado mientras no se teclee el carácter n ni el carácter s. Cuando se teclea alguno de estos caracteres, se almacena en c y se abandona el bucle.

El siguiente ejemplo es un caso de bucle while sin cuerpo.

while (getch () != 13);

El programa está detenido en esta sentencia hasta que se teclee (código ASCII 13).

El siguiente programa utiliza un bucle while para solicitar del usuario que adivine un número.

#include <stdio.h>

#include <stdlib.h>

void main ()

{

int num;

int n = 0;

randomize (); // Las funciones randomize() y random() permiten

num = random (20) + 1; // generar números aleatorios

while (n != num) {

printf ("\nTeclee un número entre 1 y 20: ");

scanf ("%d", &n);

if (n == num) puts ("ACERTASTE);

else if (n < num) puts ("TU NÚMERO ES MENOR");

else puts ("TU NÚMERO ES MAYOR");

}

}

Respecto del bucle while es conveniente tener presente lo siguiente:

  • El cuerpo del bucle no se ejecutará NUNCA si la primera vez no se cumple la condición.

  • El bucle puede ser INFINITO si no se modifican adecuadamente las variables de la condición dentro del bucle.

El bucle do/while

Tiene la forma

do

cuerpo_del_bucle;

while (expresión);

siendo sentencia una sentencia simple o un grupo de sentencias encerradas entre llaves {}, y expresión cualquier expresión válida C. El flujo de ejecución es el siguiente:

cuerpo_del_bucle

VERDADERA

expresión

FALSA

Por lo tanto, en un bucle do/while el cuerpo_del_bucle se ejecuta al menos una vez, incluso aunque expresión se evalúe como falsa, puesto que la evaluación se hace al final, justo lo contrario del bucle while, en el que la evaluación de expresión se hace al principio.

En el siguiente ejemplo se solicita un carácter del teclado hasta que se pulse cualquiera de los caracteres 'S' o 'N'.

#include <stdio.h>

void main ()

{

char tecla;

do {

printf ("\nPulse S o N: ");

tecla = getch ();

} while (tecla != 'S' && tecla != 'N');

}

Sentencia break

Es la misma sentencia que hemos visto para finalizar los case de la sentencia switch. Pero además permite forzar la salida inmediata de un bucle (for, while o do/while) en cualquier momento, ignorando el resto de sentencias. Veamos un ejemplo:

#include <stdio.h>

void main ()

{

int n;

for ( ; ; ) {

printf ("\nTeclee un número: ");

scanf ("%d", &n);

if (!n) break;

printf ("\nEl cuadrado es %d", n * n);

}

}

En este ejemplo, el bucle for se ejecutaría sin fin a no ser por la sentencia

if (!n) break; //Es lo mismo que if (n == 0) break;

Se van solicitando números por teclado y visualizando sus cuadrados hasta que se teclee un 0 (!n se evaluaría como cierto), en cuyo caso se ejecuta la sentencia break, que provoca la salida inmediata del bucle sin que se ejecute la sentencia printf del final.

Sentencia continue

Esta sentencia se utiliza en los bucles for, while y do/while. Cuando se ejecuta fuerza un nuevo ciclo del bucle, saltándose cualquier sentencia posterior. Por ejemplo, el bucle

int i, n;

...

...

for (i = 1; i <= 100; i++) {

n = i / 2;

if (i == 2 * n) continue;

printf ("\n%d", i);

}

muestra en pantalla sólo los números impares, puesto que para los números pares la expresión i == 2 * n se evalúa como cierta, ejecutándose la sentencia continue que fuerza de inmediato un nuevo ciclo del bucle.

El siguiente programa muestra como actúa la sentencia continue en un bucle do/while.

#include <stdio.h>

#include <conio.h>

void main ()

{

int n;

int positivos = 0;

clrscr ();

do {

printf ("\nTeclea un número (-99 finaliza): ");

scanf ("%d", &n);

if (n <= 0) continue;

positivos++;

} while (n != -99);

printf ("\nHas tecleado %d números positivos", positivos);

}

La sentencia positivos++ sólo se ejecuta cuando n es un número positivo. Si n es negativo o vale 0, se ejecuta continue que fuerza una nueva evaluación de la condición de salida del bucle.

Etiquetas y sentencia goto

En C existe la sentencia de salto incondicional goto que fuerza un salto del programa a una línea identificada por una etiqueta. La etiqueta se define con un identificador válido C, seguido por dos puntos (:).

goto etiqueta;

...

...

etiqueta: sentencia;

...

...

La etiqueta puede estar antes o después del goto, pero siempre en la misma función.

Realmente, en lenguajes con suficientes estructuras de control (como C) no suelen presentarse situaciones que hagan necesaria la sentencia goto. Sin embargo, en alguna ocasión puede ser conveniente, bien porque la velocidad de proceso es importante (un salto con goto es más rápido que otro tipo de controles de bifurcación), o bien porque su uso clarifica el código. El caso más habitual es la salida de varios niveles de anidamiento.

for (...) {

while (...) {

for (...) {

...

...

if (...) goto salir;

...

...

}

}

}

salir: ...

...

En este ejemplo, la única alternativa a goto sería la realización de varias comprobaciones en cada bucle que forzase sentencias break, lo cual haría más ilegible el código.

Función exit()

Esta función permite la finalización del programa en cualquier punto del mismo. Devuelve el control al sistema operativo o a otro proceso padre, enviando un valor de retorno. Necesita la inclusión del archivo de cabecera process.h, por medio de una sentencia #include.

Si en un programa escribimos

if (condición) exit (0);

se produce el final del programa cuando condición es cierta, en cuyo caso se devuelve el valor 0 al proceso padre.

El programa que se muestra a continuación (SINO.C) será ejecutado desde un archivo BAT (proceso padre). El programa SINO pasará un valor de retorno al proceso padre por medio de la función exit(). El proceso padre inspecciona este valor de retorno mediante ERRORLEVEL.

/* Programa SINO.C: Admite por teclado el carácter 's' o el carácter 'n'. En el primer caso ('s') entrega un valor de retorno 1

En el segundo caso ('n') entrega un valor de retorno 0 */

#include <stdio.h>

#include <conio.h>

#include <process.h>

void main ()

{

char letra;

do

letra = getch ();

while (letra != 's' && letra != 'n');

if (letra == 's') exit (1);

exit (0);

printf ("\nFin del programa"); //Esta sentencia no se ejecuta nunca

}

Compilamos y enlazamos este programa como SINO.EXE y lo incluimos en un archivo por lotes como el siguiente:

@ECHO OFF

ECHO Pulsa S ó N

SINO

IF ERRORLEVEL == 1 GOTO SI

GOTO NO

:SI

ECHO Pulsaste SÍ

GOTO FIN

:NO

ECHO Pulsaste NO

:FIN

@ECHO ON

Este archivo visualiza el mensaje "Pulsaste SÍ" cuando en SINO se teclea el carácter s, y visualiza el mensaje "Pulsaste NO" si en SINO se teclea el carácter n. En ningún caso se visualiza el mensaje "Fin del programa" de la última línea de SINO.C

Ejercicios

1. Escribe un programa que asigne una calificación literal a un estudiante, basada en la siguiente tabla de puntuación:

8.5 a 10 Sobresaliente

7 a 8.5 Notable

6 a 7 Bien

5 a 6 Suficiente

3.5 a 5 Insuficiente

0 a 3.5 Muy deficiente

El programa capturará un valor numérico del teclado y visualizará la calificación correspondiente. Los suspensos se mostrarán en amarillo parpadeando sobre fondo rojo, el sobresaliente en amarillo sobre fondo azul, y el resto en negro sobre fondo verde.

2. Escribe un programa para determinar si un atleta es seleccionado para correr una maratón. Para seleccionar a un corredor, debe haber terminado una maratón anterior en un determinado tiempo. Los tiempos de calificación son 150 minutos para hombres menores de 40 años, 175 minutos para hombres mayores de 40 años, y 180 minutos para mujeres. Los datos a introducir son: sexo (H/M), edad y tiempo efectuado en su anterior maratón. El programa visualizará el mensaje "Seleccionado" o "No seleccionado".

3. Los empleados de una fábrica trabajan en dos turnos: diurno y nocturno. Se desea calcular el jornal diario de acuerdo al siguiente baremo:

Las horas diurnas se pagan a 1000 pesetas.

Las horas nocturnas se pagan a 1600 pesetas.

Caso de ser domingo, la tarifa se incrementará en 400 pesetas el turno diurno y en 600 el nocturno.

4. Escribe un programa que calcule e imprima la suma de los pares y de los impares comprendidos entre dos valores A y B que se introducen por teclado (A < B).

5. Escribe un programa que calcule xn, siendo x y n dos números enteros que se introducen por teclado.

6. Escribe un programa que calcule el factorial de un número entero positivo que se introduce por teclado.

7. Escribe un programa que encuentre el primer valor N para el que la suma

1 + 2 + 3 + ... + N

excede a un valor M que se introduce por teclado.

8. Escribe un programa que calcule el primer elemento de la serie de Fibonacci que sea mayor o igual que un valor introducido por teclado. La serie de Fibonacci se define mediante:

a0 = a1 = 1 an = an-1 + an-2

9. El valor de p puede calcularse mediante la serie

p = 4 * ( 1 - 1/3 + 1/5 - 1/7 + 1/9 ...)

Escribe un programa que calcule el valor de p. Para elegir el número de términos de la serie adopta el criterio de que la diferencia absoluta entre el valor real de p (3.141592) y el valor calculado sea menor que 10-3. Crea una función que devuelva el valor absoluto de una expresión.