TEMA 7. Subprogramas y modularidad

Anuncio
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
Descargar