TEMA 2: Control de Flujo de programa 

2.0 Introducción

        En tema  anterior aprendimos a trabajar  con variables, leer
sus  valores  por  teclado,  visualizarlas  en  pantalla  y realizar
operaciones elementales con ellas.

        Los  programas  que  escribimos  hasta  ahora  se ejecutaban
secuencialmente,  es   decir,  instrucción  tras   instrucción,  sin
posibilidad  de volver  a ejecutar  una instrucción  ya ejecutada  o
evitar  la ejecución  de un  grupo de  instrucciones si  se dan unas
características determinadas.

        En este tercer tema se describirán las instrucciones que nos
permiten  escribir  programas  que  no  se  ejecuten  de  una  forma
secuencial en el sentido explicado en el párrafo anterior.


2.1 Expresiones condicionales.

        En  ciertas ocasiones  nos puede  interesar que  un programa
llegado a un  punto de su ejecución vuelva hacia  atrás y se ejecute
de  nuevo, pero  lo que  en  general  nos interesará  será que  este
regreso a una línea de terminada  de nuestro código se realice si se
cumple una  cierta condición. Por esta  razón es necesario explicar,
antes  de  comenzar  con  las  instrucciones  propiamente  dichas de
control  de flujo  de programa,  como le  indicamos al ordenador que
deseamos evaluar una condición.

        Las expresiones  que nos permiten  realizar ésto reciben  el
nombre de  expresiones condicionales o  booleanas. Estas expresiones
sólo pueden tomar dos valores:  VERDADERO (TRUE) o FALSO (FALSE). En
general un  valor de 0 indica  que la expresión es  falsa y un valor
distinto de 0 indica que la expresión es verdadera.

        Como hemos indicado se trata de expresiones condicionales, y
análogamente   a  las   expresiones  aritméticas   podemos  comparar
variables  entre sí,  constantes entre  sí (lo  cual no  es muy útil
puesto que si  conocemos los dos valores ya  sabemos la relación que
existe  entre   ambas  constantes)  y   por  supuesto  variables   y
constantes.  Además podemos  agrupar condiciones  entre sí  formando
expresiones más complejas y ayudarnos de los paréntesis para indicar
el orden de evaluación. Los operadores condicionales son:

        ==  Representa igualdad.
        !=  Representa desigualdad
        >   Mayor que.
        <   Menor que.
        >=  Mayor o igual que.
        <=  Menor o igual que.

        Podemos  encadenar distintas  expresiones condicionales, las
cuales  deben de  ir entre   paréntesis (comparamos  de dos  en dos)
utilizando los operadores:

        &&  Y lógico.
        ||  O lógico.

        Veamos un ejemplo de expresión condicional

        (a==2)||((b>=0)&&(b<=20))

        la expresión será  cierta si la variable a es  igual a dos o
si la variable b tiene un valor comprendido entre 0 y 20.


2.1.1 La instrucción if... else.

        En  inglés  if  significa  si  condicional,  por ejemplo, si
llueve me llevaré  el paraguas, else significa sino,  sino llueve me
iré a la  playa. Este es el significado  que poseen en programación.
Su sintaxis es:

        if (condición) instrucción;else instrucción;

      NOTA: La sintaxis real del  IF es la siguiente: if (condición)
bloque else bloque.

        Un  programa  ejemplo  nos  indicará  su  funcionamiento con
claridad. Supongamos que deseamos dividir dos números. El número por
el  que dividimos  no puede  ser cero,  ésto nos  daría un  valor de
infinito, provocando  un error en  el ordenador. Por  tanto antes de
dividir deberíamos de  comprobar si el divisor es  cero. El programa
sería algo como ésto:

        #include <stdio.h>

        main()
        {
        float   dividendo,divisor;

        printf ("\nDime el dividendo:");
        scanf ("%f",÷ndo);
        printf ("\nDime el divisor:");
        scanf ("%f",&divisor);

        if (divisor==0)
               printf ("\nNo podemos dividir un número por 0");
        else
               printf ("\nEl resultado es: %f",dividendo/divisor);
        }

        Como en  todas los comandos del  lenguaje C una instrucción,
en general, puede ser solamente una o un conjunto de ellas incluidas
entre llaves.

        Por último el lenguaje C dispone de un operador ternario (de
tres  elementos)  que  permite  construir  determinadas  estructuras
if-else,  en  concreto  toma  un  valor  u  otro  dependiendo de una
expresión condicional. Su sintaxis es:

        exp1 ? exp2 : exp3

        Si exp1  es cierta la  expresión tomará el  valor exp2, sino
tomará el valor exp3. Un ejemplo de su utilización:

        /* La variable z toma el valor máximo entre a y b */
        z = ( (a>b) ? a : b);

        Como  se puede  observar se  trata de  una secuencia if else
pero muy  concreta, probablemente el  compilador generará un  código
mucho más eficiente para este tipo  de secuencia de ahí su inclusión
en el juego de operadores del C.

        A  continuación  se  describirán  las  instrucciones que nos
permiten controlar el flujo de programa, en las cuales tendremos que
utilizar  expresiones condicionales  continuamente, por  lo cual  no
insistiremos más en este tema.


2.2 Control del flujo de programa

2.2.0 Introducción

        A estas  alturas el lector ya  debería conocer lo que  es el
flujo  de  programa.  El  flujo  de  programa  es  la  secuencia  de
instrucciones que  un programa ejecuta  desde su comienzo  hasta que
finaliza. En  principio la ejecución es  secuencial, comienza con la
primera instrucción  y termina con  la última. Sin  embargo es común
que nos  interese que nuestro programa  no termine con la  última de
las instrucciones (si  por ejemplo no podemos abrir  un fichero y la
función  del  programa  es  modificar  ese  fichero,  el programa no
debería realizar ninguna operación y terminar al detectar el error),
o puede que  nos interese que  un grupo de  instrucciones se ejecute
repetidamente hasta que  le indiquemos que pare. Todo  esto se puede
conseguir con las instrucciones que se describirán a continuación.


2.2.1 Creación de bucles de ejecución.

2.2.1.0 Concepto de bucle

        En la introducción  ya se ha mencionado lo  que es un bucle.
Una secuencia  de instrucciones que se  repite un número determinado
de veces o hasta que se cumplan unas determinadas condiciones.

        Los bucles son extremadamente  útiles en nuestros programas,
algunos ejemplos son:

        * Lectura/Visualización de  un número determinado  de datos,
          como por ejemplo una matriz.

        * A veces se  hace necesario introducir  esperas en nuestros
          programas ya  sea por trabajar  con un periférico  lento o
          simplemente por  ralentizar su ejecución.  Los primeros se
          llaman  bucles  de  espera  activa  y  los  segundo bucles
          vacíos.

        * En  aplicaciones   gráficas  como  trazado   de  líneas  o
          rellenado de polígonos.

        * Lectura de datos de un fichero...

        A   continuación   describiremos   las   opciones   que  nos
proporciona el lenguaje de programación C para crear y gestionar los
bucles.


2.2.1.1 Bucle for

        La primera  opción de que  disponemos es el  bucle for. Este
tipo de instrucción se halla presente en la mayoría de los lenguajes
de programación  estructurados, y permite repetir  una instrucción o
conjunto  de  instrucciones  un  número  determinado  de  veces.  Su
sintaxis es como sigue:

        for (exp1;exp2;exp3) instrucción;

        exp1  es  una  expresión  que  sólo  se  ejecuta  una vez al
principio del  bucle. El bucle  for suele utilizarse  en combinación
con un contador. Un contador es  una variable que lleva la cuenta de
las veces que se han ejecutado las instrucciones sobre las que actúa
el comando for. Por tanto exp1  suele contener una expresión que nos
permite inicializar ese contador generalmente a 0 aunque eso depende
de para qué deseemos utilizar el bucle.

        exp2 es la expresión que nos indica cuando debe finalizar el
bucle,  por  tanto  se  tratará  de  una  expresión  condicional. Su
interpretación   sería   algo   como;   repite   la  instrucción  (o
instrucciones) mientras  se cumpla exp2. Esta  expresión se evaluará
en  cada ciclo  del bucle  para determinar  si se  debe realizar una
nueva iteración.

      NOTA:  Hay que  recordar que  exp2 se  evalúa al principio del
bucle,  y no  al final.  Por tanto  es posible  no ejecutar el bucle
NINGUNA vez.

        exp3  es una  expresión que  se ejecuta  en cada  iteración.
Puesto  que como  ya indicamos  el bucle  for se  utiliza junto a un
contador,  exp3 en  general contiene  una instrucción  que actualiza
nuestro contador.

        Por tanto en un bucle  con contador distinguimos tres partes
diferenciadas:

        * La inicialización del contador (exp1).

        * La condición de fin de bucle (exp2).

        * Y la actualización del contador (exp3).

        El bucle for esta especialmente pensado para realizar bucles
basados en contadores.  Se puede utilizar en bucle  del tipo "repite
esto  hasta  que  se  pulse  una  tecla",  pero  para  estos tenemos
instrucciones más apropiadas. Veamos  unos ejemplos que nos permitan
comprender más fácilmente el funcionamiento del comando for.

        Ejemplo 1: Contar hasta diez.

        #include <stdio.h>

        main()
        {
        int i; /* Esta variable la utilizaremos como contador*/

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

        Este programa mostrará en pantalla numeros de 0 a 9 (diez en
total).  exp1 inicializa  nuestro contador  que en  este caso es una
variable de tipo  entero, con el valor 0, exp2  nos dice que nuestra
instrucción (la función printf)  debe repetirse mientras el contador
sea menor que diez y finalmente  exp3 indica que el contador debe de
incrementarse en una unidad en cada ciclo del bucle.

        Nos podría interesar contar desde diez hasta 1, en este caso
el comando for debería de ser:

        for (i=10;i>0;i--) printf ("\n%d",i);

        Ejemplo 2: Visualizar dos tablas de multiplicar en pantalla.

        #include <stdio.h>

        main()
        {
        int     i;
        int     tabla1,tabla2;

        tabla1 = 2; /* Primero la tabla del dos */
        tabla2 = 5; /* y a continuación la tabla del cinco*/

        for (i=1;i<11;i++)
                {
                   printf ("\n %2dx%2d=%3d",tabla1,i,tabla1*i);
                   printf ("   %2dx%2d=%3d",tabla2,i,tabla2*i);
                }
        }

        El  ejemplo  es  análogo  al  anterior,  pero  en  este caso
visualizamos valores desde uno a  diez, en lugar de visualizarlos de
0 a  9.  En  este  ejemplo,  el  bucle  actúa  sobre  un conjunto de
instrucciones, no  sobre una sola,  por tanto debemos  introducirlas
entre las llaves  para indicar al compilador que  la instrucción for
actúa sobre las dos instrucciones.  Estamos considerando todo lo que
se encuentre entre las llaves como una sola instrucción.

        Para terminar con los bucles  de tipo for un leve comentario
sobre los bucles añadados, simplemente lo  que se hace es incluir un
bucle dentro  de otro. Supongamos que  deseamos introducir los datos
de nuestro jugadores preferidos por  teclado para almacenarlos en el
ordenador, y que de cada  jugador queremos almacenar por ejemplo, su
nacionalidad, su  peso y su altura.  En este caso nos  sería útil un
bucle anidado.  El bucle exterior  nos contaría jugadores,  mientras
que para cada jugador tendríamos otro  bucle que nos leyera los tres
datos  que  necesitamos.  Si  tenemos  veinte  jugadores preferidos,
incluiríamos una unas instrucciones como estas:

        for (i=0;i<20;i++)
                {
                        printf ("Jugador preferido %d",i);
                        for (j=0;j<3;j++)
                                {
                                        leo característica j;
                                        la almaceno donde sea;
                                }
                }

        Nada  más  en  lo  que  a  bucles  de  tipo  for respecta. A
continuación veremos  las otras estructuras  que nos proporciona  el
lenguaje C para la realización de bucles.


2.2.1.2 Bucles while.

        La sintaxis de este bucle será algo así:

        while (exp2) instrucción;

        En  inglés  while  significa  mientras,  por  tanto la línea
anterior   significaría   mientras   de   cumpla   exp2  ejecuta  la
instrucción.  Obviamente  la  instrucción  que  ejecutemos  debe  de
permitir  que en  algún caso  se  cumpla  exp2, de  lo contrarío  el
ordenador  permanecería eternamente  ejecutando instrucción. También
es evidente que exp2 debe  ser una expresión condicional. Como vemos
este tipo  de bucles no  está orientado a  contadores, es mucho  más
genérico, sin embargo se puede utilizar  de forma análoga a for. Con
la nomenclatura utilizada anteriormente tendríamos algo como ésto:

        exp1;
        while (exp2)
                {
                        instrucción;
                        exp3;
                }

        Con  este  esquema  se  hace   patente  la  utilidad  de  la
instrucción for para bucles con  contador puesto que se "centraliza"
todo  el   proceso  de  gestión   del  contador  (inicialización   y
actualización) en  una sola instrucción. Un  error común al escribir
un bucle con contador con una estructura while es olvidar introducir
exp3, con lo cual nunca se cumple exp2 y nunca salimos del bucle. De
nuevo un bucle  infinito aunque a veces nos  interesa tener un bucle
infinito. La forma más sencilla de realizar un bucle infinito es con
la expresión:

        while (1) instrucción;

        Como  indicamos exp2  es  una  expresión condicional  y para
estas expresiones un valor distinto de 0 es verdadero por tanto un 1
es siempre cierto  y no hay manera de salir  del bucle puesto que es
una  constante  y  ninguna  modificación  de  variables por parte de
instrucción tendría repercusiones sobre ella.

        Los bucle  while son útiles en  aplicaciones como; lee datos
de este fichero  mientras no llegues al final  ó muestra estos datos
en la pantalla mientras no pulse una tecla.

        Una  peculiaridad  de  esta  instrucción  es  que  puede  no
ejecutarse  nunca  la  instrucción  afectada  por  el while. Algunos
ejemplos:

        Ejemplo 1: Contar hasta diez.

        #include <stdio.h>

        main()
        {
        int     i;

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

        El primer valor que visualizaremos será el 0. Cuando i tenga
el  valor nueve  la condición  i<10  todavía  se cumple  por lo  que
entraremos  en  el  bucle  de   nuevo,  visualizaremos  el  nueve  e
incrementamos i  con lo que pasa  a tener el valor  10 momento en el
cual se  vuelve a evaluar la  expresión i<10 que en  este caso sería
falsa y no  volveríamos a entrar en el  bloque de instrucciones (las
que están entre llaves). Así visualizamos nueve número de 0 a 9 como
antes. Si incluyésemos una instrucción para visualizar el valor de i
antes de abandonar el programa (justo antes de las últimas llaves el
valor que se mostraría sería 10.

        Ejemplo 2:  Lee números enteros  hasta que se  introduzca el
valor hasta que se introduzca el valor 0.

        #include <stdio.h>

        main()
        {
        int     numero;

        numero = 10;
        while (numero!=0)
                {
                        printf ("\nDime un número:");
                        scanf ("%d",&numero);
                }
        }

        En este ejemplo tenemos que introducir en la variable número
un valor distinto de cero antes de entrar en el bucle, puesto que en
principio  al  declarar  una  variable  el  valor  de  ésta  no está
determinado y podría valer cero, en cuyo caso nunca se ejecutaría el
bucle. Esta es la misión de la instrucción numero = 10;.


2.2.1.3 Bucle do.. while

        Su  funcionamiento  es  análogo  al  anterior,  con la única
salvedad de que la condición ahora  se evalúa después de ejecutar la
instrucción su sintaxis sería:

        do instrucción while (exp2);

        Si  en el  ejemplo  anterior  utilizamos esta  estructura no
sería  necesario actualizar  numero con  un valor  distinto de cero,
puesto que antes de comprobar si es cero leeríamos el valor.

        #include <stdio.h>

        main()
        {
        int     numero;

        do
                {
                        printf ("\nDime un numero :");
                        scanf ("%d",&numero);
                } while (numero !=0);

        La diferencia fundamental con la instrucción anterior es que
esta estructura ejecuta  la instrucción sobre la que  actúa al menos
una vez.


2.2.1.4 Modificadores del flujo de programa.

        Puede que en ciertas ocasiones no  nos interese que si se da
alguna  condición   sólo  se  ejecute  un   conjunto  de  todas  las
instrucciones sobre las  que actúa el bucle o  simplemente salir del
bucle  antes de  llegar a  la condición  que hayamos  indicado en el
comando for o en while.  Esto es posible mediante dos modificadores:
continue y break.

        El primero de ellos, continue, permite volver a reevaluar la
condición  de  salida,  es  decir,  después  de ejecutar continue la
siguiente  instrucción que  se ejecutará   será el  for o  el while.
Veamos un ejemplo de aplicación  para comprender mejor como funciona
este comando.

        #include <stdio.h>

        main()
        {
        int     numero;
        int     contador;

        contador =0;
        do
                {
                   printf ("\nIntroduce el número %2d:",contador);
                   scanf ("%d",&numero);
                   if ((numero<0)||(numero>20)) continue;
                   contador++;
                } while (contador<50);
        }

        Este programa lee números en una variable hasta un máximo de
50, alcanzado este  máximo el programa termina. Además  si el número
no está  entre 0 y 20  (si es menor que  0 o mayor que  20) vuelve a
pedir que lo introduzcamos. El comando continue en la instrucción if
obliga al programa a saltar a la instrucción while donde se vuelve a
evaluar la condición, sin pasar por la línea en la que se incrementa
el contador. De  esta forma se nos vuelve a  pedir el mismo número y
la entrada incorrecta no es tenida en cuenta.

        La función  de break es  ligeramente distinta no  salta a la
instrucción en  la que se  evalúa la condición  sino que simplemente
abandona el bucle y continúa la ejecución en la línea inmediatamente
siguiente al bucle.

        #include <stdio.h>


        main()
        {
        int     i;

        for (i=0;i<20;i++)
                {
                        if (i==5) break;
                        printf ("\n%d",i);
                }
        printf ("\n\n%d",i);
        }

        La salida de este programa sería algo como esto:

        0
        1
        2
        3
        4

        5

        Y con esto terminamos todo  lo relacionado con los bucles en
lenguaje  C. Los  bucles son  una estructura  básica y  es necesario
utilizarla en la inmensa mayoría de los programas.


2.2.2 Menús de Opciones.

2.2.2.1 Introducción

        La mayoría  de los programas permiten  realizar una serie de
operaciones sobre datos. Lo ideal sería poder indicarle al ordenador
que  operación  deseamos  realizar  sobre  estos  datos  en lenguaje
natural. Por  ejemplo, para una  base de datos  nos podría interesar
decirle al ordenador: "Buscame todas  la fichas de libros que traten
sobre  informática" ó  "Borra de  la base  de datos  el libro  tal".
Existen en la actualidad herramientas  de este tipo, pero aún distan
bastante  del  lenguaje  natural,  por  ello  una  de las formas más
sencillas de indicar al ordenador la operación que deseamos realizar
es utilizar un menú de opciones.

        La  otra  solución  comúnmente  adoptada  es  una  línea  de
comandos, es decir, escribimos una frase en un lenguaje muy reducido
indicando al ordenador lo que deseamos hacer (de una forma similar a
como  lo hacemos  en MS-DOS).  Esta solución  tiene la desventaja de
tener que aprender complicados  comandos y distintas secuencias para
distintos programas.

        Un  menú  nos  muestra  en  pantalla  todas las opciones que
podemos realizar con  nuestro programa de forma que  no es necesario
que  el   usuario  conozca  ninguna  serie   de  ordenes  complejas,
simplificando por  tanto el uso  de los programas  por parte de  los
usuarios.


2.2.2.2 Sentencia switch-case-default

        La mayoría de los lenguajes de alto nivel disponen de alguna
instrucción que  permite gestionar el  valor de una  variable de una
forma estructurada y sencilla, permitiendo  por tanto la creación de
menús  de  programa  de  una  forma  sencilla.  En  lenguaje  C esta
instrucción  es  switch-case-default.  Veamos  un  ejemplo  de  como
funciona mediante un pequeño programita.

        #include <stdio.h>

        main()
        {
        int             opcion;

        printf ("\nEjemplo de Menú de Programa");
        printf ("\n1.-Cargar fichero de datos");
        printf ("\n2.-Almacenar fichero de datos");
        printf ("\n3.-Modificar datos");
        printf ("\n4.-Salir");

        printf ("\n\nDime tu opción :");scanf ("%d",&opcion);

        switch (opcion)
                {
                        case 1:
                        /* Código para cargar fichero de datos*/
                                break;
                        case 2:
                        /* Código para almacenar datos */
                                break;
                        case 3:
                        /* Código para modificar datos */
                                break;
                        case 4:
                        /* Salir del programa */
                                exit (0);
                        default :
                        printf ("\nSu opción no está disponible");
                        printf ("\nInténtelo con otra");
                }

        Del ejemplo  se deduce fácilmente el  funcionamiento de esta
secuencia. El comando switch (var) realizará una bifurcación o salto
a la línea indicada por la variable  var. Si var vale 2, el programa
se seguirá ejecutando a partir de la línea marcada con case 2. Todos
los separadores case están separador por un comando break, ya que de
no ser así la ejecución seguiría lineal hasta encontrar la llave que
termina el comando switch. La palabra clave default indica el código
que se debería  ejecutar si el valor de  la variable especificada en
el switch no  corresponde con ninguno de los  indicados por los case
dentro del switch.  Así en nuestro ejemplo si  opcion tiene un valor
distinto  de  1,  2,  3  ó  4,  se  mostrará  en pantalla un mensaje
indicando que la opción indicada no es correcta. La sintaxis de esta
estructura sería:

        switch (variable)
        {
                case valor1-variable:
                        código asociado;
                case valor2-variable:
                        código asociado;
                .
                .
                case valorN-variable:
                        código asociado;
                default:
                        código asociado;
        }

        Dentro del código asociado a  cada opción se deberán incluir
las instrucciones  break adecuadas. Ya se  explicó el funcionamiento
de break y continue cuando se  habló de bucles. Su funcionamiento es
análogo para los comandos for, while, do-while, y switch.

        Un  fragmento de  código para  la imprementación  de un menú
completo sería:

        while (Opcion!=0)
        {
        /* Secuencia de printfs que muestran en pantalla
           el menú. En este caso la opción 0 debería ser salir */
                switch (opcion)
                {
                        /* Secuencia de cases */
                        default :
                                /* Mostrar mensaje de error */
                }
        }

        Por su puesto las aplicaciones  del comando switch van mucho
más  allá de  la simple  creación de  menús. Puede  ser utilizada en
cualquier  aplicación  en  la  que  se  necesite  realizar distintas
operaciones dependiendo de un valor.  Un ejemplo sencillo podría ser
un un programa que imprimiese  textos en impresora. Podríamos marcar
en el texto mediante una secuencia especial como debería ser impreso
el texto a continuación. Por ejemplo:

        @N    - Activa Negrita.
        @n    - Desactiva Negrita.
        @C,@c - Activa/desactiva cursiva.
        @S,@s - idem sibrayado
        etc...

        Leeríamos  estos valores  provenientes del  teclado o  de un
fichero  y con  algo de  procesamiento y  una instrucción switch con
dicha variable, en cada case enviaríamos a la impresora la secuencia
adecuada para realizar cada una de la opciones.