TEMA 7. Subprogramas y modularidad 1 Niveles de abstracción __________________________________ 2 2 Solución de problemas utilizando técnicas de diseño descendente (refinamientos sucesivos) _____________________________________ 3 3 Estilo en la creación de un programa______________________ 4 3.1 Programación Modular _____________________________ 4 3.2 Programación Estructurada _________________________ 4 4 Subprogramas: Procedimientos y funciones ________________ 5 5 Parámetros y variables locales. Variables globales __________ 10 5.1 Ambito o alcance de las variables____________________ 10 5.2 Variables locales__________________________________ 10 5.3 Parámetros (por valor y por referencia) ______________ 12 5.4 Variables globales ________________________________ 14 6 Prototipos de funciones ________________________________ 15 7 Módulos ____________________________________________ 16 8 Ámbito de las funciones _______________________________ 17 9 Proyectos ___________________________________________ 18 10 Sección de includes: Ficheros cabecera _________________ 18 11 Compilación y enlazado (link) _________________________ 21 12 APENDICE: Visión general de un programa en C ________ 25 Página 1 de 1 1 Niveles de abstracción La solución de cualquier problema puede darse en varias formas o niveles de abstracción. Comenzaremos el proceso de solución dando un enunciado más general o abstracto de la solución del problema. Después, tendremos que refinar esta solución elaborando los detalles que se habían ignorado previamente, de lo que resulta una solución nueva que es muchísimo menos abstracta. Este proceso continúa a través de un cierto número de etapas cada vez más refinadas, hasta que se ha logrado un nivel de detalle apropiado. Esta es la esencia del diseño top-down. Esta técnica top-down se ha denominado de muy diversas formas: refinamiento por pasos, modelado iterativo multinivel, y programación jerárquica. Imprimase una lista de los cuadrados perfectos entre 1 y N Léase un número N Imprímanse los cuadrados perfectos entre 1 y N Compílese un vector con los cuadrados perfectos Imprimase dicho vector entre 1 y N ¿Es el número en proceso un cuadrado perfecto? Insértese en la siguiente posición disponible del vector Figura 4. Solución por el método top-down del problema de los cuadrados perfectos. Página 2 de 2 2 Solución de problemas utilizando técnicas de diseño descendente (refinamientos sucesivos) Para afrontar un problema complejo, no hay nada más práctico que descomponerlo en problemas más simples, hasta llegar a un nivel que seamos capaces de resolver directamente. Esta es la idea del diseño descendente o diseño top-down. A cada uno de estos subproblemas le vamos asociando un subprograma o módulo que codifica la solución para dicho subproblema. El algoritmo, resuelto de esta forma, se puede representar por un árbol en que cada nodo es un módulo (un problema o acción hipotética). Así, construimos el programa de abajo a arriba, creando primero procedimientos que resuelvan los módulos de detalle, que una vez comprobados serán usados por otros procedimientos más generales, hasta llegar a la creación del programa original. Una ventaja fundamental del diseño descendente es su adaptación a la programación estructurada. Página 3 de 3 3 Estilo en la creación de un programa A la hora de programar se deben seguir ciertos criterios, o estilos de programación, que faciliten la elaboración del programa, su posterior modificación, la reutilización del código y su legibilidad por parte de otros programadores que no son los propios creadores. Para conseguirlo: Estructurada. Programación Modular y 3.1 Programación Modular Con la programación modular el programa se divide en módulos (partes independientes), cada una de las cuales ejecuta una actividad o tarea y se codifican independientemente. Cada programa contendrá un módulo principal que controla y transfiere el control a submódulos (subprograma), y estos a su vez cuando finalizan, devuelven el control al módulo principal. Al ser los módulos independientes, diferentes programadores pueden trabajar simultáneamente en diferentes partes del mismo programa. Esto reduce el tiempo del diseño del algoritmo y su codificación. Además podemos modificar un módulo radicalmente sin afectar a otros módulos. 3.2 Programación Estructurada Cualquier programa sufre durante su vida modificaciones. Un buen programa debe, por tanto, estar escrito de tal modo que las modificaciones se realicen en él Página 4 de 4 de forma cómoda y rápida. Un primer requisito para esto es que el programa sea legible (lo es cuando una persona distinta de quien lo ha escrito, puede comprender fácilmente su funcionamiento al leerlo). Un lenguaje, para permitir la creación de programas estructurados, debe disponer de las estructuras de control ciclo condicional y decisión estructurada, además de sentencias con transferencia secuencial. 4 Subprogramas: Procedimientos y funciones Para realizar un programa grande, podemos dividirlo en partes más pequeñas (subprogramas), cada una de las cuales realiza una tarea determinada. Estos subprogramas serán llamados desde el programa principal mediante una instrucción de llamada. Esta es la base de la programación estructurada y el diseño descendente. Los subprogramas ofrecen tres ventajas: 1. Mejoran la legibilidad del programa. 2. Acortan los programas, ya que evitan tener que escribir repetidamente las correspondientes instrucciones. 3. Permiten que puedan ser reutilizados en otros programas, con lo que se acorta el tiempo de realización de éstos. Un subprograma puede ser utilizado tantas veces como se desee en distintas partes del programa o en otros programas. Los subprogramas pueden ser de dos tipos: Página 5 de 5 • Procedimientos: son subprogramas que realizan una tarea determinada pero no devuelven ningún valor. Se utilizan para estructurar un programa y mejorar su claridad y generalidad. • Funciones: son subprogramas que realizan una determinada tarea y devuelve un valor. Se utilizan para crear operaciones nuevas no incluidas en el lenguaje. En C no se distingue entre funciones y procedimientos: todos son funciones. Un programa C está formado por varias funciones, una de las cuales se llama main(), y hace de programa principal. main() es el punto de entrada al programa, es la 1ª función que se ejecuta y desde ella se llama al resto de funciones del programa. Las funciones que componen un programa pueden agruparse en uno o varios ficheros. Un programa puede residir en varios módulos, los cuales a su vez pueden compilarse por separado y cargarse junto con bibliotecas de funciones previamente compiladas. Formalmente, una función tiene la siguiente sintaxis: static tipo_devuelto nombre_funcion(declaracion_parametros) { directivas declaraciones; sentencias; return valor_devuelto;} Página 6 de 6 donde: • static (opcional): indica que la función tiene alcance de módulo. Si omitimos static, la función tiene alcance global. • tipo_devuelto (opcional): tipo (por defecto int) devuelto por la función. Si la función no devuelve nada, se indica con void. • nombre_funcion función. (obligatorio): identificador de la • declaracion_parametros (opcional): lista argumentos pasados a la función. Los ( ) son obligatorios aunque la lista este vacía • Las llaves de bloque ‘{‘ y ‘}’ delimitan el cuerpo de la función. • return (opcional): Permite salir de la función o devolver un valor a la sentencia que la llamó. Puede haber varios return. • valor_devuelto (opcional): valor que devuelve la función. Debe ser del mismo tipo que el especificado en tipo_devuelto. La lista de declaración de parámetros tiene la siguiente sintaxis: static tipo funcion(tipo var1, tipo var2, . . ., tipo varN) Todos los argumentos de la lista tienen que incluir tanto el tipo como el nombre, incluso si hay varios parámetros del mismo tipo. Página 7 de 7 funcion(int x, int y, char c) /* correcto */ funcion(int x, y, char c) /* incorrecto */ Por defecto, las funciones tienen alcance global, a menos que se declaren como static que indican que tienen alcance de módulo. Es posible omitir el tipo de los argumentos en la declaración de los parámetros, pero debe indicarse en una sentencia, antes de ‘{‘. Este forma de declaración (llamada formato clásico) está obsoleta. /* Formato actual */ /* Formato clásico */ int f(int a, int b, char c) int f(a, b, c) { int a,b; ... } char c; { ... } Las funciones: 1. Tienen tiempo de vida global. 2. Tienen alcance global, excepto las static (alcance de módulo) 3. No pueden anidarse (no puede definirse dentro de otra). 4. Pueden ser recursivas y una función puede llamar a otra 5. Una función no puede ser destino de una asignación. ordenar(x, y)= 20; /* incorrecto */ Página 8 de 8 Todas las funciones, excepto las void, devuelven un valor (en el return) y pueden ser usadas como operando en cualquier expresión /* max, raiz, cubo y cuadrado devuelven un valor */ x = max(y, z) - raiz(9) + 7; if (cubo(x) > cuadrado(y)) z = cubo(x); Una función void no puede usarse en expresiones o asignaciones. void func(int a); /* prototipo de función */ y = func(x); /* no hay ningun valor para asignar a y */ z = func(5) – 6; /* no hay valor que sumar a 6 */ Aunque una función devuelva un valor, no es obligatorio asignárselo a una variable en la sentencia que llama a la función: #include <stdio.h> int max(int a, int b); /* prototipo de funcion */ int main() { int x, y, maximo; printf(“\nDame dos enteros”); scanf(“%d”,&x); scanf(“%d”,&y); maximo = max(x,y); /* el valor de max se asigna a maximo*/ printf(“El valor mayor de %d y 50 es %d”, maximo, max(maximo,50)); max(5,7); /* el valor de max se pierde al no asignarse */ return 0;} int max(int a, int b) { if (a > b) return (a) else (b); } Página 9 de 9 5 Parámetros y variables locales. Variables globales En C hay tres tipos de variables, dependiendo del alcance, visibilidad y tiempo de vida que éstas tengan: Variables locales, parámetros y variables globales. 5.1 Ambito o alcance de las variables Ambito local: Variable declarada dentro de un bloque o de una función. Sólo puede ser usada en la función donde esta definida. Ambito de módulo: Variable declarada como static fuera de cualquier bloque o función. Sólo puede ser usada por las variables y funciones del módulo, no por el resto de módulos. Ambito global: Variable declarada a nivel externo al principio de un módulo y redeclarada a nivel externo (con extern) en el resto de módulos del programa. Es visible y accesible por todas las variables y funciones de todos los módulos que forman el programa. 5.2 Variables locales Las variables locales son aquellas que están definidas dentro de una función o de un bloque. No pueden utilizarse fuera del bloque o función donde están definidos. Se crean cuando el flujo entra en el bloque en el que están definidas y se destruyen al salir. La excepción son las locales estáticas (static). Sus contenidos no se pierden una vez que se deja el bloque donde están. Página 10 de 10 Una función puede tener variables locales con el mismo nombre que variables globales. La variable local tienen preferencia. Podemos declarar variables en cualquier parte; sólo pueden ser usadas una vez declaradas y no antes. Una variable no es creada hasta que el flujo llega a la sentencia donde se hace la declaración: int a=1,b=1,c=1; int main() { printf("A: %d B: %d C: %d\n",a,b,c); /* a, b, c globales */ ver(); printf("A: %d B: %d C: %d\n",a,b,c); /* a, b, c globales */ return 0; } void ver(void) { int a=5; /* a local */ c++; /* incrementa c global */ printf("a: %d b: %d c: %d\n",a,b,c); /* a local, b y c global */ int c=50; /* c local, a partir de aquí, las referencias a c son a la local */ c++; /* incrementa c local */ printf("a: %d b: %d c: %d\n",a,b,c); /* a y c local, b global */ } Página 11 de 11 5.3 Parámetros (por valor y por referencia) Los parámetros son los valores que se pasan a la función al ser llamada. Cada parámetro funciona dentro de la función como si de una variable local se tratara: se crean al entrar en la función y se destruyen al salir de ésta (tienen tiempo de vida y ámbito local). Los parámetros que se escriben en la sentencia de llamada a la función se llaman parámetros reales o argumentos. Los parámetros que aparecen en la descripción de la función se llaman parámetros formales. Los parámetros formales son sustituidos por los parámetros reales y el orden de sustitución es el de llamada. Deben coincidir en número y tipo, aunque no es necesario que sus nombres coincidan. De no ser así se producirán errores o resultados inesperados: #include <stdio.h> int suma(unsigned char a, unsigned char b); int main() { printf("La suma es %d\n",suma(258,3); return 0; } int suma(unsigned char a, unsigned char b) { return(a+b); } Al compilar no hay errores, pero el resultado devuelto es: Página 12 de 12 La suma es 5 El 258 en binario es: 00000001 00000010 El 3 en binario es: 00000011 Como el compilador esperaba un char como primer argumento, del 258 coge solo el primero de los dos bytes que lo forma y lo interpreta como un 2. Por tanto suma 3 y 2, devolviendo un 5. Los parámetros pueden ser de dos tipos: • Parámetros por valor o copia: Sólo se pasa una copia del valor del argumento; los cambios realizados en la función no afectan a la variable argumento usada en la llamada • Parámetros por variable o referencia: Se transfiere la dirección de memoria del argumento, la cual se utiliza para acceder a la variable usada como argumento en la llamada, con lo que los cambios afectan al parámetro real (argumento) C usa el método de llamada por valor para pasar argumentos. Para forzar una llamada por referencia es necesario usar punteros: #include <stdio.h> int main() { int x=5; /* variable global */ printf("El valor de x es %d\n",x); int triple(x) { printf("El triple de x es %d\n",triple(x)); x = x * 3; return(x); printf("El valor de x es %d\n",x); } cambia(&x); void cambia(int *x) { printf("El valor de x es %d\n",x); *x=*x + 2;} return 0} Página 13 de 13 Tras ejecutar el programa, el resultado mostrado es el siguiente: El valor de x es 5. El triple de x es 15. El valor de x es 5. El valor de x es 7 En la llamada a la función triple sólo se transfiere el valor de la x (5). La variable global x, no se ve afectada y conserva su valor. En la llamada a la función cambia, se transfiere la dirección de memoria de la variable x, no su valor. Al modificar el valor de dicha dirección de memoria (*x = *x + 2) modificamos la variable global x. 5.4 Variables globales Las variables globales son accesibles y visibles en todo el programa y se pueden usar en cualquier lugar (y función) del programa. La excepción son las variables globales estáticas: sólo son accesibles y visibles en el módulo en el cual están definidas. Las variables globales tienen tiempo de vida global. Se declaran a nivel externo. Cualquier función puede acceder y modificar su valor. No es necesario pasarla como parámetro a una función. Deben evitarse abusar de las variables globales ya que ocupan memoria durante toda la ejecución del programa y pueden ser modificadas por cualquier función. Página 14 de 14 6 Prototipos de funciones Informan al compilador del nº y tipos de datos de los argumentos de una función así como del tipo que ésta devuelve. Sirven para: 1. Evitar las conversiones erróneas de los valores cuando los argumentos y parámetros no son del mismo tipo. 2. Evitar errores en el número y tipo de argumentos usados al llamar y/o definir una función. Los prototipos van al principio del programa. Su sintaxis es: static tipo_devuelto nombre_funcion(tipo var1, . . ., tipo varN); Es como la 1ª línea de la definición de función, añadiendo ‘;’. int min(int x, int y); /* prototipo de funcion */ int main() { float a=29.57,b; printf("Dame un número"); scanf("%f",&b); printf("El número mas pequeño es %d\n",min(a,b)); return 0;} int min(int x, int y) { return(x < y ? x : y); } Al llamar a min(), pasamos argumentos float, cuando la función espera unos int. Como hemos puesto el prototipo de la función, se realiza previamente una conversión de los Página 15 de 15 valores float a int; el valor de a (29.57) se convierte en 30, lo mismo que el de b. De no existir el prototipo, los valores a y b serian enviados como float (4 bytes), y min() los hubiera interpretado como int (2 bytes): los valores de x e y serian absurdos, así como su resultado. • funcion1(void) parámetros. -> indica que función1 no tiene • funcion1() -> no indicamos ninguna información sobre los tipos o la cantidad de parámetros de función1. No indica que no tenga. 7 Módulos Una de las ventajas que ofrece el lenguaje C es que permite la posibilidad de escribir un programa en múltiples ficheros o módulos. Un programa puede residir en uno o más archivos fuente (módulos), cada uno de los cuales contiene funciones y variables relacionadas. Estas funciones existentes en un determinado módulo pueden ser visibles al resto del programa o no dependiendo si son declaradas como estáticas (static) o no. Las variables globales de un módulo pueden ser visibles o no al resto de módulos de la aplicación. Las globales static sólo pueden ser usadas en el módulo en el cual están definidas. El resto son accesibles en todos los módulos si son declaradas como global en uno de ellos y redeclaradas como extern en el resto. Página 16 de 16 Las ventajas de dividir un programa en diferentes módulos son: 1. Se facilita la edición. Un archivo grande es difícil de editar, mientras que uno más pequeño es más fácil de mantener. 2. La compilación de la aplicación se realiza más rápidamente. En un programa dividido en múltiples módulos, sólo se compilan los módulos en los que se han producido cambios. 3. La programación es más estructurada. Al situar funciones y variables relacionadas en un mismo fichero aumentamos la comprensión del programa y disminuimos los errores. 4. Aumentamos y promovemos la reusabilidad del software: El código objeto del módulo puede ser enlazado al de cualquier nuevo programa, sin tener que rediseñar y recompilar el código, haciendo la programación más rápida y eficiente. 8 Ámbito de las funciones Las funciones tienen dos ámbitos: ámbito global y de módulo. Por defecto, las funciones tienen alcance global, a menos que se declaren como static que indican que tienen alcance de módulo. Una función de ámbito o alcance global puede ser usada en cualquiera de los ficheros (módulos) que componen la Página 17 de 17 aplicación sin necesidad de ser declaradas en el resto de fichero donde se usan. Una función static (alcance de módulo) sólo puede ser usada en el fichero (módulo) donde está definida y no es accesible en el resto de módulos que componen la aplicación. 9 Proyectos En C, los programas de archivos o módulos múltiples se llaman proyectos. Cada proyecto se asocia a un fichero de proyecto .prj, que determina que ficheros son parte del proyecto. Un proyecto normalmente comprende un fichero proyecto .prj, uno o más ficheros cabecera .h y dos o más ficheros fuente .c. Cuando hay un fichero de proyecto .prj, el compilador lee el contenido del archivo .prj y compila cada archivo allí indicado. Una vez creado los ficheros .obj, se enlazan y se crea el programa ejecutable .exe con el mismo nombre que el fichero proyecto. 10 Sección de includes: Ficheros cabecera En un programa C, debemos incluir los prototipos de las funciones de la biblioteca estándar de C que usemos. Estos prototipos están especificados en distintos ficheros cabecera .h suministrados con el propio compilador. Para poder utilizar la biblioteca de C es necesario insertar su respectivo archivo cabecera Estos archivos contienen, además de funciones: prototipos de Página 18 de 18 1. Definiciones, macros y ctes que usan las funciones (#define). 2. Referencias externas (variables extern de otros módulos). 3. Enumeraciones (enum) y declaración de estructuras (struct). Los archivos cabecera se insertan al principio del programa fuente con la directiva #include y a continuación el nombre o ruta completa del fichero. Un fichero fuente puede tener varios #include. Además de los ficheros cabecera suministrados por el compilador, el usuario puede definir los suyos propios con los prototipos y funciones que el mismo haya implementado. La directiva #include tiene dos tipos de sintaxis. #include <nombre_fichero.h> Los símbolos ‘<’ y ‘>’ indica al compilador que busque el fichero en el directorio include indicado en el entorno integrado de C. #include "nombre_con_ruta_acceso.h" Cuando va entre comillas el compilador busca el fichero en la ruta de acceso especificada, en el directorio por defecto del sistema y en los directorios especificados en el comando path del DOS. Página 19 de 19 El uso de ficheros cabecera ofrece las siguientes ventajas: 1. Cuando es necesaria una nueva declaración o redefinición, ésta sólo se hace en el fichero .h. Los módulos, al contener la directiva #include automáticamente quedan actualizados. 2. Cuando un fichero .obj es utilizado en mas de un programa, el fichero .h contiene las declaraciones y definiciones necesarias para que ese módulo pueda ser incluido en los nuevos ficheros fuente, simplificando así el desarrollo. 3. Una vez creado y depurado un fichero o módulo, puede ser usado en cualquier proyecto añadiendo un #include con el nombre del fichero .h que contiene sus prototipos y constantes y enlazando el modulo objeto .obj con el fichero. Ejemplo: supongamos un programa compuesto de dos módulos (modulo1 y modulo2) que hacen uso de una serie de constantes definidas en un fichero de cabecera llamado cab.h. (ambos módulos contendrán una sentencia #include cab.h al principio del fichero). Para realizar el ejecutable, creamos un fichero de proyecto (.prj) con los nombres de los dos módulos que componen el programa. El fichero proyecto es usado por el compilador como guía: cada archivo especificado en el archivo .prj es compilado. Una vez creado los ficheros objetos .obj, se Página 20 de 20 enlazan y se crea el ejecutable .exe que tendrá el mismo nombre que el fichero proyecto .prj. 11 Compilación y enlazado (link) La compilación es el proceso mediante el cual el código fuente, escrito en un determinado lenguaje de programación, es convertido a un código objeto, directamente entendible por la computadora. Durante la compilación, el compilador chequea que no se produzcan errores. De producirse alguno no se generará Código fuente fichero .c Compilador Código objeto Librerías fichero .obj Códigos de arranque Enlazador Código ejecutable fichero .exe el correspondiente fichero objeto .obj. Una vez compilados y generados todos los ficheros objetos .obj entra en acción el enlazador o linkador. El enlazador (linker) realiza las siguientes funciones: • Combina los archivos .obj de la lista de enlace, con los ficheros objetos del sistema (códigos de arranque) Página 21 de 21 y con las librerías del sistema (.lib) y genera un archivo ejecutable. • Resuelve las referencias externas y direcciones de memoria. Las referencias externas son producidas cuando el código de un archivo hace referencia al código situado en otro (bien porque llame a una función definida en otro módulo, o bien porque haga referencia a una variable extern). Durante el proceso de enlace (link) pueden producirse errores si el linkador no es capaz de encontrar algún fichero .obj y .lib o si se producen inconsistencia entre los diferentes ficheros objetos (por ejemplo, una variable extern definida en un fichero no está definida en ninguno de los demás módulos, o no coinciden sus tipos). Examinemos el siguiente programa, compuesto de dos módulos: /* MODULO 1 */ #include <stdio.h> static int a=6; int b; extern int c; /* prototipo de funciones */ void ver(void); int incr(); Página 22 de 22 int main() { printf("a: %d; b: %d; c: %d\n",a,b,c); incr(); ver(); return 0;} /* MODULO 2 */ #include<stdio.h> extern int a; int b=1; void ver(void) { printf("a: %d; b: %d\n",a,b); } Al compilar cada uno de estos módulos no se produce ningún error de compilación. El compilador se tiene que ‘fiar’ de lo indicamos en cada módulos ya que no puede saber a priori si las funciones y variables externas especificadas en cada módulo están declaradas e implementadas en el resto de módulos. Esto sólo puede ser resuelto en el momento del enlazado del programa. Cuando el enlazador entra en acción y trata de resolver las diferentes referencias externas se producen estos errores: Linker Error: _b defined in MODULO1.CPP duplicated in MODULO2.CPP Linker Error: Undefined symbol incr() in module MODULO1.CPP Página 23 de 23 Linker Error: Undefined symbol _c in module MODULO1.CPP Linker Error: Undefined symbol _a in module MODULO2.CPP 1. En ambos módulos está duplicada la variable b: En ambos hemos usado una variable global con el mismo nombre (b). 2. La función incr() no está definida en ningún módulo: En la compilación no se produjo error por que el compilador ‘esperaba’ que dicha función estuviera definida en el otro módulo o en la biblioteca de funciones del C. 3. La variable c no está definida en ningún sitio: En MODULO1 indicamos que existe una variable global externa c definida en otro módulo, pero en MODULO2 no aparece definida. 4. La variable a no está definida en ningún sitio: En MODULO2 indicamos que existe una variable global a externa definida en otro módulo, pero en MODULO1 la variable global a es estática, por tanto sólo es visible en dicho módulo. Recuerda: además de errores en tiempo de compilación y de ejecución pueden producirse errores en tiempo de linkado o enlace. Una vez compilado los diferentes módulos y linkados correctamente se generará el correspondiente fichero ejecutable. Página 24 de 24 12 APENDICE: Visión general de un programa en C Un programa C puede estar compuesto por uno o más módulos. Cada módulo puede contener: • Directivas: incluir ficheros (#include), definir ctes (#define) • Declaraciones (prototipos de funciones, variables, etc). • Funciones. Cada función puede contener: directivas, declaraciones y uno o más bloques de sentencias. • Cada bloque pueden contener: declaraciones y una o más sentencias. directivas, • Un programa debe tener al menos una función (main()). En un programa con múltiples módulos sólo existe una main(). Página 25 de 25 • Programa C de múltiples Módulos Programa C Módulo Módulo (fichero fuente) (fichero fuente) Directivas Directivas Declaraciones Declaraciones Main( ) Función Bloque … Bloque ... Función Bloque … Bloque ... Bloque … { directivas declaraciones sentencias } Bloque … Bloque ... Función Bloque … Bloque La estructura de cada módulo debe ser la siguiente: 1. Cada módulo debe comenzar con un comentario que describa que hace el módulo. Debe incluirse el nombre del módulo, versión, fecha de 1ª realización y última modificación 2. A continuación debemos poner las diferentes directivas #include para incluir los prototipos, macros y constantes de los ficheros de librería y del propio Página 26 de 26 usuario que usamos, así como los prototipos que no vienen allí especificados. 3. A continuación debemos poner las diferentes directivas #define de las constantes y macros usadas en el módulo. 4. Posteriormente las definiciones de las variables globales, globales static, y variables extern definidas en otros módulos (si no viene incluidas en ningún fichero cabecera). 5. Por último vendrán las diferentes funciones que componen el módulo. Éstas deben tener un comentario previo que describan que tarea realizan y que parámetros recibe, si la función modifica alguna variable global, así como la índole y el tipo de valor devuelto por la función (si devuelve alguno). Página 27 de 27