CAPITULO 4: PROPOSICIONES PARA EL CONTROL DE FLUJO DE PROGRAMA

 

1. INTRODUCCION

En lo que sigue de este capítulo, denominaremos BLOQUE DE SENTENCIAS al conjunto de sentencias individuales incluídas dentro un par de llaves. Por ejemplo :

{
sentencia 1 ;
sentencia 2 ;
.............
sentencia n ;
}

Este conjunto se comportará sintacticamente como una sentencia simple y la llave de cierre del bloque NO debe ir seguida de punto y coma .
Un ejemplo de bloque ya visto , es el cuerpo del programa principal de la función main() .

main()
{
bloque de sentencias
}

En las proposiciones de control de flujo de programa , trabajaremos alternativamente con sentencias simples y bloques de ellas .

2. PROPOSICION IF - ELSE

Esta proposición sirve para ejecutar ciertas sentencias de programa , si una expresión resulta CIERTA ú otro grupo de sentencias, si aquella resulta FALSA. Su interpretación literal sería : SI es CIERTA tal cosa , haga tal otra , si no lo es salteéla .

El caso más sencillo sería :

if(expresión)
 sentencia ;

ó

if(expresión)  sentencia ;

Cuando la sentencia que sigue al IF es única, las dos formas de escritura expresadas arriba son equivalentes . La sentencia sólo se ejecutará si el resultado de "expresión" es distinto de cero (CIERTO) , en caso contrario el programa salteará dicha sentencia , realizando la siguiente en su flujo.


Veamos unos ejemplos de las distintas formas que puede adoptar la "expresión" dentro de un IF :

 

if( a > b ) if( (a > b) != 0 ) las dos expresiones son idénticas, aunque a veces resulta más claro expresarla de la segunda manera, sobre todo en los primeros contactos con el lenguaje.
if(a) if(a != 0)
if(!a) if(a == 0 ) Las dos superiores son idénticas entre sí , al igual que las dos inferiores Obsérvese que (!a) dará un valor CIERTO sólo cuando a sea FALSO. (ver operador NEGACION en el capítulo anterior )
if( a == b )
if( a = b ) /* Error */ La primera es una expresión correcta , el IF se realizará sólo si a es igual a b. En cambio la segunda es un error , ya que no se está comparando a con b , sino ASIGNANDO el valor de esta a aquella . Sin embargo, a veces puede usarse como un truco (un poco sucio) de programacion , ya que primero se realiza la asignación y luego se evalúa el resultado de esta para realizar el IF , es entonces equivalente a escribir :
a = b ;
if(a) .................... con el ahorro de una linea de programa ( a costa de la legibilidad del mismo ).

 

En casos más complejos que los anteriores , la proposición IF puede estar seguida por un bloque de sentencias :

if(expresión)                        if(expresión) {

{                                          sentencia 1 ;

sentencia 1 ;                        sentencia 2 ;

sentencia 2 ;                        ...............

.............                        }

}

Las dos maneras son equivalentes , por lo que la posición de la llave de apertura del bloque queda librada al gusto del programador . El indentado de las sentencias (sangría) es también optativo , pero sumamente recomendable ,sobre todo para permitir la lectura de proposiciones muy complejas ó anidadas , como se verá luego. El bloque se ejecutará en su conjunto si la expresion resulta CIERTA. El uso del ELSE es optativo , y su aplicación resulta en la ejecución de una , ó una serie de sentencias en el caso de que la expresión del IF resulta FALSA.

 

Su aplicación puede verse en el ejemplo siguiente :

   if(expresión)                 if(expresión)

   {                             {

   sentencia 1 ;                 sentencia 1 ;

   sentencia 2 ;                 sentencia 2 ;

   }                             }

   sentencia 3 ;                 else

   sentencia 4 ;                 {

   sentencia 5 ;                 sentencia 3 ;

                                 sentencia 4 ;

                                 }

                                 sentencia 5 ;

En el ejemplo de la izquierda no se usa el ELSE y por lo tanto las sentencias 3 , 4 y 5 se ejecutan siempre . En el segundo caso , las sentencias 1 y 2 se ejecutan solo si la expresión es CIERTA , en ese caso las 3 y 4 NO se ejecutarán para saltarse directamente a la 5 , en el caso de que la expresión resulte FALSA se realizarán las 3 y 4 en lugar de las dos primeras y finalmente la 5 .
La proposición ELSE queda siempre asociada al IF más cercano , arriba de él .
Es común también , en caso de decisiones múltiples , el uso de anidamientos ELSE-IF de la forma indicada abajo:

if(exp.1)                        if(exp.1)

 sentencia1 ;                     sentencia1 ;

else  if(exp.2)                  else  if(exp.2)

 sentencia2 ;                          sentencia2 ;

else if(exp.3)                         else if(exp.3)

 sentencia3 ;                                sentencia3 ;

else                                   else

 sentencia5 ;                                sentencia5 ;

Si bién se suele escribir según la modalidad de la izquierda , a la derecha hemos expresado las asociaciones entre los distintos ELSE é IF por medio del indentado del texto.

 

3. PROPOSICION SWITCH
El SWITCH es una forma sencilla de evitar largos , tediosos y confusos anidamientos de ELSE-IF .
Supongamos que estamos implementando un Menu , con varias elecciones posibles . El esqueleto de una posible solución al problema usando if-else podría ser el siguiente :


#include <<stdio.h>>

main()

{

int c  ;

printf("\nMENU :") ;

printf("\n       A = ADICIONAR A LA LISTA ") ;

printf("\n       B = BORRAR DE LA LISTA   ") ;

printf("\n       O = ORDENAR LA LISTA     ") ;

printf("\n       I = IMPRIMIR LA LISTA    ") ;

printf("\n\nESCRIBA SU SELECCION , Y LUEGO <<ENTER>> : ") ;

if( (c = getchar()) != '\n' )

 {

  if( c == 'A')

    printf("\nUD. SELECCIONO AGREGAR") ;

  else

    if( c == 'B')

     printf("\nUD. SELECCIONO BORRAR") ;

    else

     if( c == 'O' )

      printf("\nUD. SELECCIONO ORDENAR") ;

     else

      if( c == 'I' )

       printf("\nUD. SELECCIONO IMPRIMIR") ;

      else

       printf("\n\a\aUD. APRETO UN CARACTER ILEGAL" ) ;

 }

 else

  printf("\n¡ UD. NO HA SELECCIONADO NADA !" ) ;

}


Como es fácil de ver , cuando las opciones son muchas, el texto comienza a hacerse difícil de entender y engorroso de escribir.
El mismo programa, utilizando un SWITCH , quedaría mucho más claro de leer, y sencillo de escribir, como se aprecia en el EJEMPLO siguiente.


#include <stdio.h>

#include <conio.h> 

main()
{

int c  ;

printf("\nMENU :") ;

printf("\n       A = ADICIONAR A LA LISTA ") ;

printf("\n       B = BORRAR DE LA LISTA   ") ;

printf("\n       O = ORDENAR LA LISTA     ") ;

printf("\n       I = IMPRIMIR LA LISTA    ") ;

printf("\n\nESCRIBA SU SELECCION , Y LUEGO <<ENTER>> : ") ;

c = getchar() ;

switch (c)

 {

  case 'A' :

    printf("\nUD. SELECCIONO AGREGAR") ;

    break ;

  case 'B' :

    printf("\nUD. SELECCIONO BORRAR") ;

    break ;

  case 'O' :

    printf("\nUD. SELECCIONO ORDENAR") ;

    break ;

  case 'I' :

    printf("\nUD. SELECCIONO IMPRIMIR") ;

    break ;

  case '\n':

    printf("\n¡ UD. NO HA SELECCIONADO NADA !" ) ;

    break ;

  default  :

    printf("\n\a\aUD. APRETO UN CARACTER ILEGAL" ) ;

    break ;

 }

}

El SWITCH empieza con la sentencia : switch (expresión) . La expresión contenida por los paréntesis debe ser ENTERA , en nuestro caso un caracter ; luego mediante una llave abre el bloque de las sentencias de comparación . Cada una de ellas se representa por la palabra clave "case" seguida por el valor de comparación y terminada por dos puntos . Seguidamente se ubican las sentencias que se quieren ejecutar , en el caso que la comparación resulte CIERTA . En el caso de resultar FALSA , se realizará la siguiente comparación , y así sucesivamente .
Prestemos atención tambien a la sentencia BREAK con la que se termina cada CASE. Una característica poco obvia del SWITCH , es que si se eliminan los BREAK del programa anterior , al resultar CIERTA una sentencia de comparación, se ejecutarán las sentencias de ese CASE particular pero TAMBIEN la de todos los CASE por debajo del que ha resultado verdadero. Quizás se aclare esto diciendo que , las sentencias propias de un CASE se ejecutarán si su comparación ú otra comparación ANTERIOR resulta CIERTA . La razón para este poco "juicioso" comportamiento del SWITCH es que así se permite que varias comparaciones compartan las mismas sentencias de programa , por ejemplo :

 .................

 case 'X' :

 case 'Y' :

 case 'Z' :

  printf(" UD. ESCRIBIO X , Y , ó  Z ") ;

  break ;

 ..................

La forma de interrumpir la ejecución luego de haber encontrado un CASE cierto es por medio del BREAK , el que dá por terminado el SWITCH .
Al final del bloque de sentencias del SWITCH , aparece una optativa llamada DEFAULT , que implica : si no se ha cumplido ningun CASE , ejecute lo que sigue. Es algo superfluo poner el BREAK en este caso , ya que no hay más sentencias despues del DEFAULT , sin embargo , como el orden en que aparecen las comparaciones no tiene importancia para la ejecución de la instrucción, puede suceder que en futuras correcciones del programa se agregue algún nuevo CASE luego del DEFAULT , por lo que es conveniente preveerlo , agregando el BREAK , para evitar errores de laboriosa ubicación .
Más adelante volveremos sobre otros usos del BREAK.


4. LA ITERACION WHILE
El WHILE es una de las tres iteraciones posibles en C . Su sintaxis podría expresarse de la siguiente forma :

 while(expresion)           ó           while(expresión) {

   proposición 1 ;                      proposición 1 ;

                                        proposición 2 ;

                                        ...............

                                        proposición n ;

                                        }

Esta sintaxis expresada en palabras significaria: mientras (expresión) dé un resultado CIERTO ejecútese la proposición 1 , en el caso de la izquierda ó ejecútese el bloque de sentencias , en el caso de la derecha.
Por lo general , dentro de la proposición ó del bloque de ellas , se modifican términos de la expresión condicional , para controlar la duración de la iteración .

5. LA ITERACION DO - WHILE
Su sintaxis será :

do {

 proposición 1 ;

 proposición 2 ;

 ...............

} while (expresión) ;

Expresado en palabras , esto significa : ejecute las proposiciones , luego repita la ejecución mientras la expresión dé un resultado CIERTO . La diferencia fundamental entre esta iteración y la anterior es que el DO-WHILE se ejecuta siempre AL MENOS una vez , sea cual sea el resultado de expresión.

6. ITERACION FOR
El FOR es simplemente una manera abreviada de expresar un WHILE , veamos su sintaxis :

for ( expresión1 ; expresión2 ; expresion3 ) {

        proposición1 ;

        proposición2 ;

        ..............

}

Esto es equivalente a :

expresión1 ;

while ( expresión2 ) {

        proposición1 ;

        proposición2 ;

        ..............

        expresion3   ;

}

La expresión1 es una asignación de una ó más variables , (equivale a una inicialización de las mismas ) , la expresión2 es una relación de algun tipo que , mientras dé un valor CIERTO , permite la iteración de la ejecución y expresión3 es otra asignación , que comunmente varía alguna de las variables contenida en expresión2 .
Todas estas expresiones , contenidas en el paréntesis del FOR deben estar separadas por PUNTO Y COMA y NO por comas simples .
No es imprescindible que existan TODAS las expresiones dentro del paréntesis del FOR , pudiendose dejar en blanco algunas de ellas , por ejemplo :

for ( ; exp2 ; exp3)             ó

for (exp1 ; ; )                  ó

for ( ; ; )

 

Estas dos últimas expresiónes son interesantes desde el punto de vista de su falta de término relacional , lo que implica que el programador deberá haber previsto alguna manera alternativa de salir del lazo ( probablemente mediante BREAK ó RETURN como veremos más adelante ) ya que sinó , la ejecución del mismo es infinita ( ó tan larga como se mantenga encendida la computadora ) .


7. LA SENTENCIA BREAK
El BREAK , ya brevemente descripto con el SWITCH , sirve también para terminar loops producidos por WHILE , DO-WHILE y FOR antes que se cumpla la condición normal de terminación . En el EJEMPLO siguiente vemos su uso para terminar un WHILE indeterminado.


#include <stdio.h>

#include <conio.h>

main()

{

  char c ;

  printf("ESTE ES UN LOOP INDEFINIDO ") ;

  while(1)  {

   printf( "DENTRO DEL LOOP INDEFINIDO (apriete una tecla):" ) ;

   if( (c = getch()) == 'Q' )

    break ;

   printf( "\nNO FUE LA TECLA CORRECTA PARA ABANDONAR EL LOOP ") ;

  }

   printf("\nTECLA CORRECTA : FIN DEL WHILE ") ;

}

Obsérvese que la expresión while(1) SIEMPRE es cierta , por lo que el programa correrá imparable hasta que el operador oprima la tecla "secreta" Q . Esto se consigue en el IF , ya que cuando c es igual al ASCII Q se ejecuta la instrucción BREAK ,dando por finalizado el WHILE .
El mismo criterio podría aplicarse con el DO-WHILE ó con FOR , por ejemplo haciendo

 for (;;)  {     /* loop indefinido */

 ............

  if( expresión )

   break ;       /* ruptura del loop cuando expresión sea verdadera */

 }

8. LA SENTENCIA CONTINUE
La sentencia CONTINUE es similar al BREAK con la diferencia que en vez de terminar violentamente un loop , termina con la realización de una iteración particular y permitiendo al programa continuar con la siguiente.

9. LA FUNCION EXIT()
La función EXIT() tiene una operatoria mucho más drastica que las anteriores , en vez de saltear una iteración ó abandonar un lazo de programa , esta abandona directamente al programa mismo dándolo por terminado . Realiza también una serie de operaciones útiles como ser , el cerrado de cualquier archivo que el programa hubiera abierto , el vaciado de los buffers de salida , etc.
Normalmente se la utiliza para abortar los programas en caso de que se esté por cometer un error fatal é inevitable . Mediante el valor que se le ponga en su argumento se le puede informar a quien haya llamado al programa ( Sistema Operativo , archivo .bat , u otro programa ) el tipo de error que se cometió.

10 SENTENCIA GOTO
Si Ud. se ha admirado de que C tenga la operación GOTO , recuerde que el hecho de existir NO lo obliga a usarla , en el mismo sentido que por tener puertas los aviones no está obligado a saltar por ellas en pleno vuelo.
El uso del GOTO implica un salto incondicional de un lugar a otro del programa . Esta práctica hace que los programas sean muy dificiles de corregir ó mantener.
Si no quedara más remedio que usarlo, (y en programación estructurada SIEMPRE hay remedio) debe marcarse el destino del salto mediante un nombre seguido por dos puntos .

if( c == 0 )  goto OTRO_LADO ;
.............................
OTRO_LADO:
 printf(........

En este caso si c es cero se saltean todas las sentencias entre el if y el destino , continuandose con la ejecución del printf() . El destino puede ser tanto posterior como anterior al GOTO invocante .




Siguiente Capitulo
Volver Al Indice