Artículos‎ > ‎

Programación modular en C

La parte que más liosa me resulta del lenguaje C es la que corresponde a la programación modular.
Como a veces estoy largas temporadas sin programar en este lenguaje, cada vez que tengo que retomar algún proyecto me cuesta bastante volver a asimilar estos conceptos.
Es por ello que he decidido escribir este mini-tutorial, y también por si de paso, pudiera interesarle a alguien más :-)


Introducción

Si estamos desarrollando un programa en C y vemos que nuestro único fichero fuente empieza a crecer y crecer, es hora de replantearse realizar un diseño modular y dividir el programa en varios ficheros fuente o módulos.

Cada módulo se encargará de una función concreta: dibujar en pantalla, capturar el teclado, generar sonidos, etc... y se compilará por separado para enlazarse posteriormente y generar el ejecutable.

Aunque la distribución del código en varios ficheros fuentes no es muy complicada puede generar problemas al principio, ya que introduce nuevos conceptos que antes se pasaban por alto, como interfaz, ficheros de cabecera, variables globales, etc...

Interfaz: Un poco de Teoría

En términos generales de programación, la interfaz define los métodos de comunicación que un módulo hace públicos para que los demás se comuniquen con él.

Por norma general la interfaz se debe mantener aparte de la implementación del módulo para separar la comunicación interna con la externa. De esta forma, si cambiamos la implementación pero dejamos intacta la interfaz, los demás módulos podrán seguir comunicándose de la misma manera con nuestro módulo.

Veamos la utilidad de esto: imaginemos que tenemos un programa en C con varios módulos y uno de ellos está poco optimizado o presenta fallos. Si somos capaces de mejorar o arreglar el código de ese módulo sin modificar su interfaz, no tendremos que realizar modificaciones en el resto de los módulos que se comunican con él.

Archivos de cabecera

En C, la interfaz de un módulo son las funciones, variables, tipos de datos y constantes que se hacen públicas al resto de los módulos.

Por lo tanto, por cada módulo modulo.c existirá un fichero de cabecera modulo.h, que contendrá la declaración de la interfaz de ese módulo.
Los elementos que son de uso interno del módulo no se deben declarar en el fichero cabecera. 

Los módulos que vayan a utilizar recursos de un módulo llamado modulo.c tendrían que incluir su fichero de cabecera con #include "modulo.h" con lo que se incluyen las declaraciones públicas de ese módulo. 

Normalmente es el programador que ha creado la librería quien proporciona el fichero de cabecera. Para declarar las funciones y variables externas de la librería, el usuario que quiera usar la librería simplemente deberá incluir el fichero de cabecera.

Variables globales

Imaginemos que queremos compartir una variable entre varios módulos. Las opciones que tenemos son dos:

  • Pasar esa variable como parámetro.
  • Utilizar variables globales.

Si tenemos muchas variables que queremos compartir entre módulos, la primera opción redundaría en largas listas de parámetros para cada función. Por ello, la opción más cómoda y eficiente suele ser usar variables globales.

Para que una variable sea global se debe definir (sólo una vez) fuera de cualquier función. A su vez, debe ser declarada en cada módulo que quiera acceder a la variable.

Para evitar problemas de ambigüedad entre definición y declaración de una variable global es conveniente que a la hora de declarar la variable global usemos el calificador extern. Con ello le estamos diciendo al compilador que estamos solamente declarando la variable y que la definición estará en algún otro lado (en otro fichero o después en el mismo fichero).

Nota sobre variables static:
El significado del calificador static variará dependiendo de si se aplica a una variable global o interna:

  • Variable global: Limita la visibilidad de la variable al fichero fuente que estamos compilando
  • Variable interna: funciona igual que una variable automática con la única diferencia de que el valor de la variable permanece aunque la variable pierda su visibilidad.

Ejemplo de Archivo de Cabecera

Imaginemos un módulo movimiento.c encargado del movimiento de un juego.

Este módulo tiene 2 funciones MoverPulga() y MoverEnemigo() que queremos que sean accesibles desde otros módulos. Además tiene una función llamada AplicarGravedad() que es de uso interno, es decir, sólo la usarán las funciones de este módulo.

Imaginemos que además el módulo tiene dos variables: posicionPulga que queremos que sea accesible desde otros módulos y la variable rapidezPulga, que es de uso interno a la función MoverPulga().

Nuestro archivo de cabecera movimiento.h quedaría de la siguiente forma:

[movimiento.h]

void MoverPulga()
void MoverEnemigo()
extern int posicionPulga

Como vemos sólo hemos declarado los elementos que queremos hacer públicos al resto de los módulos.

Para poder usar estas funciones y variables desde otro módulo deberemos declararlas primero. Como ya están declaradas en movimiento.h es más cómodo escribir simplemente #include "movimiento.h" (es decir, estamos incluyendo el código del fichero movimiento.h a nuestro código) que andar declarándolas una a una.
Comments