ESTRUCTURAS DE CONTROL.







Introducción.
Hasta el momento, únicamente se han construido programas en los que el flujo de código era secuencial. Ciertamente, es casi imposible una actividad real en que se tenga un flujo puramente secuencial: como mínimo, se producen circunstancias distintas pero previstas, y hay que responder adecuadamente a cada una de ellas. Por tanto es necesario disponer de sentencias de control que permitan tomar decisiones, y ejecutar uno u otro segmento de código en función de las condiciones del momento. Esta es la misión de la sentencia if() else en C.

Sentencia if()else


if (condición) /* 1 */
 {
  Bloque; /* 2 */
 }
else
 {
  Bloque; /* 3 */
 }


La representación gráfica del flujo de programa de la sentencia if() else hace fácil comprender su actuación desde el punto de vista del flujo de control. La sentencia if() else posee tres partes bien diferenciadas:

La expresión de control se evalúa al llegar el control a la sentencia if() ; su valor podrá ser nulo (falso) o no nulo (verdadero). Si el valor de la expresión de control es verdadero, se ejecutará el bloque [2]; en caso contrario, se ejecutará el bloque [3]. Las llaves son opcionales únicamente cuando el bloque en cuestión ([2] o [3]) contiene una sola sentencia. Y la cláusula else es opcional . Un primer ejemplo de uso de la sentencia if()-else podría ser el siguiente:
#include<stdio.h>

int main(int argc, char * argv[])
{
  int a;
  int b;
  int condicion;
  /* no mire esto no mire esto no mire esto no mire esto */
  if (3 == argc)
    {
       a = strtol(argv[1], NULL, 10);
       b = strtol(argv[2], NULL, 10);
    }
  else
    {
      /* mire esto mire esto mire esto mire esto */
      a = 1;
      b = 2;
    }

  condicion = a < b;

  if (condicion)
    printf("Este bloque se ejecuta porque %d < %d\n", a, b);

  condicion = a > b;

  if (condicion)
    printf("Este bloque se ejecuta porque %d es mayor que %d\n", a, b);
  else
    printf("Pero este otro bloque se ejecuta porque %d no es mayor que %d\n", a, b);

  return 0;
}

Comentario.- El if() no tiene cláusula else y sólo hace algo cuando es a < b. El segundo if() tiene una cláusula else y siempre se ejecuta uno u otro bloque. Los resultados de la ejecución de este programa son como sigue:
[cauldron:04xx_PROGS]> ./a.out
Este bloque se ejecuta porque 1 < 2
Pero este otro bloque se ejecuta porque 1 no es mayor que 2
[cauldron:04xx_PROGS]> ./a.out 2 1
Este bloque se ejecuta porque 2 es mayor que 1
[cauldron:04xx_PROGS]>


La primera ejecución, como puede verse, hace uso de los valores predefinidos dentro del programa. La segunda traduce los valores dados a través de la línea de órdenes y los emplea como argumentos, calculando el valor de condición en función de ellos. De este modo se puede experimentar con el programa para cerciorarse de que realmente se ejecutan los bloques implicados por los valores dados. También puede ser interesante ver este otro Ejemplo .

Bloques.
Es frecuente emplear la sentencia if() para decidir la posible ejecución de bloques de código. Estos bloques irán encerrados entre llaves. Un ejemplo sencillo puede ser la resolución de una ecuación de segundo grado, en que será conveniente (aunque no necesario - ¿sabrá hacerlo el lector?) emplear varias sentencias en cada bloque. Insistimos: es imprescindible emplear llaves para delimitar los bloques en la sentencia if() . Si no se emplean llaves por error, se tienen varios casos. Si las llaves omitidas corresponden al bloque else ([3]), entonces el compilador no puede detectar el error; interpretará que la única sentencia del bloque [3] es la primera (exclusivamente) que aparezca inmediatamente después de else . Entonces la ejecución será correcta para el caso en que la sentencia de control sea falsa, porque se ejecutará el contenido (incorrecto) del bloque [3], y todas las sentencias posteriores. Sin embargo, la ejecución será incorrecta para el caso en que la sentencia de control sea verdadera, porque se ejecutará el bloque [2] y también todas las sentencias del bloque [3], a excepción de la primera. Si el bloque [3] está vacío, se prescindirá por completo de la cláusula else (conjunto formado por else y el bloque asociado).

Anidamiento
No se impone restricción alguna sobre las sentencias contenidas en el bloque ([2], [3]). Por tanto, se admiten las sentencias de control, y en particular las sentencias alternativas. Se dice entonces que se admite el anidamiento de sentencias alternativas. El compilador no impondrá restricciones sobre la profundidad de anidamiento de sentencias alternativas; sin embargo, se considera de mal estilo una profundidad de anidamiento superior a tres niveles. Como ejemplo de anidamiento de sentencias alternativas, se puede escribir un programa para resolver ecuaciones cuadráticas.

Ejemplo 1 .- Construir un programa que muestre el comportamiento de la sentencia if()-else cuando la expresión de control es un número entero. Verificar que es posible prescindir de llaves ("{" y "}") cuando el contenido del bloque es una sola sentencia.
/*
 Este programa muestra la utilización de la sentencia if() else.
*/

#include <stdio.h>

int n;

int main(void)
{
 if(1)
  printf("La expresión de control se evalúa en el sentido lógico.\n\n");

 n = 1;

 if (n>0)
  printf("%2d es mayor que 0.\n", n);
 else
  printf("%2d no es mayor que 0.\n",n);

 n = -1;

 if (n>0)
  printf("%2d es mayor que 0.\n", n);
 else
  printf("%2d no es mayor que 0.\n",n);


 return 0;
}

/*
 RESULTADO
La expresión de control se evalúa en el sentido lógico.

1 es mayor que 0.
-1 no es mayor que 0.
*/

Ejemplo 2 .- Construir un programa capaz de resolver ecuaciones de segundo grado. No se consideran los casos en que a , b o c tomen valores nulos.

/*
 Este programa permite resolver ecuaciones de segundo grado.
 Sirve como ejemplo de anidamiento de estructuras if() else.
*/

#include <stdio.h>
#include <math.h> /* Imprescindible - contiene sqrt() */

float a, /* coeficiente de x^2 */
 b, /* coeficiente de x */
 c, /* término independiente */
 d; /* discriminante */
float r1,r2, /* raíces reales y distintas */
 preal, pimaginaria, /* Raíces complejas conjugadas */
 rrd; /* Raíz real doble */

int main(void)
{

   printf("Resolución de ecuaciones de segundo grado, ax^2 + bx + c = 0.\n\n");

   printf("Escriba los coeficientes a, b y c separados por espacios: ");
   scanf("%f %f %f", &a, &b, &c);
   printf("\nLa ecuación es %fx^2",a);
   b > 0.0 ? printf(" +") : printf(" ");
   printf("%fx",b);
   c > 0.0 ? printf(" +") : printf(" ");
   printf("%f = 0.", a, b, c);
   printf("\n\nSolución");
   d = b*b - 4.0*a*c;
   if (d > 0.0) /* discriminante positivo, raíces reales y distintas */
   {
     r1 = (-b + sqrt(d))/(2.0*a);
     r2 = (-b - sqrt(d))/(2.0*a);
     printf("\n\nRaíces reales y distintas: r1 = %f, r2 = %f", r1, r2);
   }
    else
   {
     if (d < 0.0) /* discriminante negativo, raíces complejas conjugadas */
     {
       preal = (-b)/(2.0*a);
       pimaginaria = sqrt(-d)/(2.0*a);
       printf( "\n\nRaíces complejas conjugadas: %f+j%f, %f-j%f",
       preal, pimaginaria, preal, pimaginaria);
     }
     else
     {
       rrd = (-b)/(2.0*a);
       printf("\n\nUna raíz real doble: rrd = %f", rrd);
     }
   }
  puts("\n\nTerminación normal del programa.\n");
  return 0;
}
/*
 COMENTARIOS

 Obsérvese que no se han estudiado todos los valores posibles de a, b y c.
 Si alguno o todos son ceros, se producen casos especiales que no se han
 tratado. Este programa es correcto, pero NO es robusto.
*/
Modificación .- Construir una nueva versión del programa anterior, adaptándolo de tal modo que admita sus parámetros a través de la línea de órdenes

Una posible soluciÛn serÌa la siguiente:
#include <stdio.h>
#include <stdlib.h> /* Imprescindible para utilizar strtof */
#include <math.h> /* Imprescindible - contiene sqrt() */

float a, /* coeficiente de x^2 */
b, /* coeficiente de x */
c, /* término independiente */
d; /* discriminante */

float r1,r2, /* raíces reales y distintas */
preal, pimaginaria, /* Raíces complejas conjugadas */
rrd; /* Raíz real doble */

int main(int argc, char * argv[])
{

  printf("Resolución de ecuaciones de segundo grado, ax^2 + bx + c = 0.\n\n");

  if (4 != argc)
    {
      printf("\n\nUtilización: ./resuelve a b c\n\n");
      return 0;
    }
  a = strtof(argv[1], NULL);
  b = strtof(argv[2], NULL);
  c = strtof(argv[3], NULL);
  printf("\nLa ecuación es %fx^2",a);
  b > 0.0 ? printf(" +") : printf(" ");
  printf("%fx",b);
  c > 0.0 ? printf(" +") : printf(" ");
  printf("%f = 0.", a, b, c);
  printf("\n\nSolución");
  d = b*b -4.0*a*c;
  if (d > 0.0) /* discriminante positivo, raíces reales y distintas */
    {
      r1 = (-b + sqrt(d))/(2.0*a);
      r2 = (-b - sqrt(d))/(2.0*a);
      printf("\n\nRaíces reales y distintas: r1 = %f, r2 = %f", r1, r2);
    }
  else
    {
      if (d < 0.0) /* discriminante negativo, raíces complejas conjugadas */
        {
          preal = (-b)/(2.0*a);
          pimaginaria = sqrt(-d)/(2.0*a);
          printf( "\n\nRaíces complejas conjugadas: %f+j%f, %f-j%f",
          preal, pimaginaria, preal, pimaginaria);
        }
      else
        {
          rrd = (-b)/(2.0*a);
          printf("\n\nUna raíz real doble: rrd = %f", rrd);
        }
    }
  puts("\n\nTerminación normal del programa.\n");
  return 0;
}
Comentarios .- Este programa muestra la forma de insertar parámetros a través de la línea de órdenes. A diferencia del anterior, la función main() admite los parámetros argc y argv , a través de los cuales se pasa información desde el sistema operativo hasta el programa. En el caso que nos ocupa, es preciso pasar al programa tres números reales, que corresponderán a los argumentos argv[1] , argv[2] y argv[3] respectivamente (el nombre del programa corresponde a argv[0] ). Estos valores deben traducirse después al formato binario, lo cual se hace mediante llamadas a la función strtol .
Una vez hecha la traducción a binario, el programa prosigue sin cambio alguno respecto a la versión anterior.

Ejercicios propuestos

  1. Ejercicio 0401r01.- Construir un programa capaz de resolver ecuaciones de segundo grado. El programa debe ser robusto, esto es, será capaz de resolver la ecuación aunque los coeficientes tomen valor nulo.

  2. Ejercicio 0401r02.- Escribir un programa capaz de calcular determinantes 2x2 y 3x3. Emplear el método de desarrollo por una línea.

  3. Ejercicio 0401r03.- Construir un programa que resuelva un sistema de dos ecuaciones con dos incógnitas empleando determinantes.

  4. Ejercicio 0401r04.- Construir un programa que resuelva un sistema de dos ecuaciones con dos incógnitas empleando determinantes. El programa debe ser robusto, esto es, detectará sistemas sin solución y se lo comunicará al usuario.

  5. Ejercicio 0401r05.- Escribir un programa que resuelva sistemas 2x2 por sustitución.

  6. Ejercicio 0401r06.- Escribir un programa que resuelva sistemas 3x3 por sustitución.

  7. Ejercicio 0401r07.- Escribir un programa que busque las raíces de una ecuación de segundo grado por métodos aleatorios. Nota . La idea es emplear la función srandom() para generar un número aleatorio comprendido entre los dos que marcan los extremos del intervalo estudiado. Se van generando parejas de números hasta encontrar una en que haya cambio de signo: entonces tiene que haber una raíz en ese intervalo. Se reduce allí el intervalo de búsqueda, y esta prosigue hasta que la distancia entre puntos sea menor que una cantidad dada.

  8. Ejercicio 0401r08.- Escribir un programa que calcule la integral de una función por métodos aleatorios. Nota . Búsquese en Internet el Método de Montecarlo. La idea es considerar un rectángulo que contiene la curva considerada. Se generan puntos aleatorios en este rectángulo, y luego se cuentan los puntos generados y los que caen bajo la curva. La relación entre el número total de puntos generados y la superfice total del rectángulo es la misma que la relación entre el número de puntos que han quedado bajo la curva y la superficie bajo la misma, que es la integral buscada.