Ejercicios resueltos laboratorio 10 - Procedimientos y funciones (complejos)

Asignatura: Fundamentos de Informática
Especialidad: Electrónica - UPV/EHU
Curso académico: 2013-2014
Profesor: Ismael Etxeberria Agiriano

Se resuelven ejemplos de escasa complejidad algorítmica (como todos los de este laboratorio) pero cierto detalle a la hora de entender el paso de parámetros.

En estos casos vamos a realizar entradas/salidas (del teclado y a pantalla) pero en general las funciones serán de cálculo. Habrá que tener mucho cuidado a la hora de interpretar las especificaciones y no confundir entradas/salidas con parametros de entrada o salida y con el valor devuelto por una función.

Enunciado del ejercicio: bLee

Diseña el diagrama de flujo de la cabecera y codifica una función que lea un número complejo en forma binómica, utilizando el siguiente prototipo:

    void bLee (double *x, double *y);

Resolución bLee

La cabecera se muestra en la figura 10.1.


Figura 10.1. Cabecera de la función bLee.

La codificación de esta función no tiene complejidad algorítmica pero hay que prestar especial atención en el paso de parámetros a la función scanf. Como nuestra función ya dispone de la dirección de las variables donde queremos dejar el resultado no le volveremos a pasar estas direcciones:


#include <stdio.h>

void bLee (double *x, double *y)
{
  printf ("Introduce la parte real: ");
  scanf ("%lf", x); /* Cuidado, no enviamos &x */
  printf ("Introduce la parte imaginaria: ");
  scanf ("%lf", y); /* Cuidado, no enviamos &y */
}

Enunciado del ejercicio: bEscr

Diseña el diagrama de flujo de la cabecera y codifica una función que escriba un número complejo en forma binómica, utilizando el siguiente prototipo:

    void bEscr (double x, double y);

Resolución bEscr

La cabecera se muestra en la figura 10.2.


Figura 10.1. Cabecera de la función bLee.

Este ejemplo ilustra bien el error común de confundir parámetros de salida y salida (escritura de datos en pantalla). Obsérvese en la cabecera que no hay parámetros de salida, aunque por la naturaleza de la función escribimos datos en pantalla.

Por estética vamos a diferenciar dos casos según la parte imaginaria sea positiva o negativo ya que si no lo hiciéramos, en el segundo caso nos mostraría dos signos.


#include <stdio.h>

void bEscr (double x, double y)
{
  if (y >= 0) printf ("%.2lf + %.2lfi ", x,  y);
  else        printf ("%.2lf - %.2lfi ", x, -y);
}

Programa principal

Para poder comprobar la corrección de las funciones propuesas se proporciona un programa completo.

Obsérvese la estructura típica del diseño descendente en la que primero vienen los prototipos de las funciones, luego el programa principal y finalmente la defición de las funciones, que repetimos por claridad.


/* 10-eje.c (versión laboratorio)*/
#include <stdio.h>
#include <stdlib.h>

void bLee  (double *x, double *y);
void bEscr (double  x, double  y);

int main (void)
{
  double x, y;

  bLee (&x, &y);
  bEscr (x, y);

  system ("pause");
  return 0;
}

void bLee (double *x, double *y)
{
  printf ("Introduce la parte real: ");
  scanf ("%lf", x); /* Cuidado, no enviamos &x */
  printf ("Introduce la parte imaginaria: ");
  scanf ("%lf", y); /* Cuidado, no enviamos &y */
}

void bEscr (double x, double y)
{
  if (y >= 0) printf ("%.2lf + %.2lfi ", x,  y);
  else        printf ("%.2lf - %.2lfi ", x, -y);
}

Organización en módulos

Si las funciones son suficientemente genéricas lo típico es crear una biblioteca (librería) que podremos linkar siempre que se utilicen y especificar los prototipos en un fichero específico de cabeceras, por ejemplo, complejos.h. De esa manera se guardará la consistencia con el tipo de los parámetros.

Así, el programa principal visto antes podría dividirse en tres ficheros:

El fichero de cabeceras con los prototipos podría ser:


/* complejos.h */
void bLee  (double *x, double *y);
void bEscr (double  x, double  y);

A continuación ilustraremos cómo sería el código correspondiente a la definición de las funciones en complejos.c.

Siendo más detallistas vamos a aprovechar para mejorar bEscr y distinguir más casos distintos de visualización, según la parte real y/o la imaginaria sean positivas, nulas o negativas. La versión revisada, incluyendo la definición de bLee se muestra a continuación:


/* complejos.c */
#include <stdio.h>
#include "complejos.h"

void bLee (double *x, double *y)
{
  printf ("Introduce la parte real: ");
  scanf ("%lf", x); /* Cuidado, no enviamos &x */
  printf ("Introduce la parte imaginaria: ");
  scanf ("%lf", y); /* Cuidado, no enviamos &y */
}

void bEscr (double x, double y)
{
  if      (x != 0 && y >  0) printf ("%.2lf+%.2lfi", x,  y);
  else if (x != 0 && y <  0) printf ("%.2lf-%.2lfi", x, -y);
  else if (x != 0 && y == 0) printf ("%.2lf",        x);
  else if (x == 0 && y != 0) printf (      "%.2lfi",     y);
  else                       printf ("0");
}

En este fichero hemos incluido stdio.h ya que estamos utilizando llamadas a scanf y a printf. De hecho, si el programa principal no utiliza directamente estas funciones scanf y printf no necesitará incluir stdio.h.

A continuación se proporciona un ejemplo de programa principal que utiliza las funciones anteriores. Es un programa típico de prueba que permitirá estudiar el comportamiento de bEscr con todas las combinaciones de números negativos, nulos y positivos tanto para la parte real como para la imaginaria.


/* 10-main.c  (versión laboratorio) */
#include <stdio.h>
#include <stdlib.h>
#include "complejos.h"

int main (void)
{
  double x, y;
  int i, j;

  for (i = -1; i <= 1; i++)
    for (j = -1; j <= 1; j++) {
      bEscr (i, j);
      printf ("\n");
    }
  bLee (&x, &y);
  bEscr (x, y); printf ("\n");

  system ("pause");
  return 0;
}

La función bEscr no escribe un salto de línea ya que es posible que en un programa que sea necesario poner dos números complejos en la misma línea. Por ello en este ejemplo llamamos directamente a printf y tenemos que incluir stdio.h.

Compilados complejos.o y 10-main.o habrá que enlazarlos (linkarlos) para obtener un único ejecutable.

Esta labor la realiza el entorno de trabajo DevC++ cuando definimos un proyecto que incluye a los dos anteriores.