tema 12

27
Tema 12 Utilización de más de un fichero fuente Fundamentos de Programación I.E.S. Juan de Garay

Upload: fresymetal

Post on 03-Jun-2015

721 views

Category:

Travel


0 download

TRANSCRIPT

Page 1: Tema 12

Tema 12Utilización de más de un fichero fuente

Fundamentos de Programación

I.E.S. Juan de Garay

Page 2: Tema 12

Tema 12: Utilización de más de un fichero fuente

1. Introducción.2. Decidir qué incluir en un fichero fuente.3. Crear un fichero de proyecto en Borland C y DEV

C++.4. Las referencias externas.5. Utilización de funciones externas.6. Estructura de ficheros cabecera para módulos

externos.7. Qué incluir en un fichero de cabecera.8. Utilización de las directivas de compilación

condicional en los ficheros de cabecera.9. Ejercicios.

Page 3: Tema 12

1. Introducción

A medida que se crean programas mayores y más potentes habrá mayor dificultad a la hora de guardar todas las funciones en un mismo fichero fuente. Se debe agrupar las funciones que realizan tareas similares en el mismo fichero fuente. Luego se puede compilar cada fichero independientemente de todos los demás para crear un proyecto.

La división de un proyecto en pequeños módulos tiene las siguientes ventajas: El seguimiento de la lógica del programa es más sencillo. Si

todo el código de un gran proyecto está incluido en un solo fichero fuente, el tamaño de éste hace que el programa sea difícil de seguir. Al dividir el programa en trozos más pequeños, de forma lógica, se consigue que sea más fácil ver el diseño general del mismo.

Los procesos de modificación y depuración del programa son más sencillos. Si se tiene dividido el programa en varios ficheros fuente es más sencillo resolver un programa. Se puede encontrar más fácilmente el grupo de funciones que están causando el problema.

Agrupando las funciones implementadas en un mismo fichero fuente será fácil poderlas volver a utilizar en otros proyectos.

Page 4: Tema 12

2. Decidir qué incluir en un fichero fuente

Cuando se empieza a agrupar las funciones en ficheros fuente debemos asegurarnos de que todas las que estén relacionadas se encuentran en el mismo fichero. Cada función debe encontrar los datos que necesita en el mismo fichero fuente en el que está definida.

Se deben agrupar las funciones de forma que se tengan grupos de funciones que se puedan incluir en otros programas fácilmente.

Al dividir un programa se debe recordar que sólo puede tener un fichero fuente con la función main (). Se puede incluir el número de funciones que se quiera dentro de cada fichero fuente, pero uno, y solamente uno, podrá contener la función main (). Algunos proyectos se construyen de forma que un fichero principal contenga la función main () siendo su única labor la de llamar a las otras funciones a medida que se vayan necesitando.

Page 5: Tema 12

3. Crear un fichero de proyecto en Borland C

Cuando se quiere implementar un programa que utiliza más de un fichero fuente se necesita crear un archivo de proyecto adecuado. Un fichero de proyecto es una lista de los ficheros fuente necesarios y otra información necesaria para crear un fichero ejecutable.

La creación de un proyecto conlleva tres pasos básicos. El primero es crear un fichero del proyecto nuevo, el segundo es añadir los ficheros fuente y el tercero crear el fichero exe.

Para acceder a las funciones de gestión de proyectos utilizar la opción Project de la barra de menú. Para crear un fichero de proyecto nuevo seleccionar el comando Open del menú Project. Con el comando Open se puede abrir un proyecto existente o asignarle un nombre a uno nuevo. Después de seleccionar Open, aparece la ventana de diálogo Load Project File (carga de fichero de proyecto). En la ventana de diálogo se tiene que teclear el nombre y la ruta del fichero de proyecto.

Una vez abierto el fichero de proyecto se puede acceder a varias opciones nuevas del menú Project: Add item (añadir elemento), Delete item (borrar elemento), ... Para comenzar a añadir ficheros fuente al proyecto seleccionado, utilizar Add item. Seleccionar el botón Done cuando no se quiera añadir más ficheros.

Page 6: Tema 12

3. Crear un fichero de proyecto en Dev C++

Cuando se quiere implementar un programa que utiliza más de un fichero fuente se necesita crear un archivo de proyecto adecuado. Un fichero de proyecto es una lista de los ficheros fuente necesarios y otra información necesaria para crear un fichero ejecutable.

La creación de un proyecto conlleva tres pasos básicos. El primero es crear un fichero de proyecto nuevo, el segundo es añadir los ficheros fuente y el tercero crear el fichero exe.

Para crear un fichero de proyecto nuevo utilizar la opción Archivo Nuevo Proyecto de la barra de menú. En la ficha Basic seleccionar el icono Empty project. En nombre del proyecto escribir el nombre y seleccionar el botón de opción En C. En la siguiente ventana seleccionar la ruta donde guardareis el fichero de proyecto con extensión .dev.

Una vez abierto el fichero de proyecto se puede acceder a varias opciones nuevas del menú Proyecto: Añadir a proyecto (añadir ficheros con extensión .c), Borrar del proyecto (quitar ficheros fuente), ... Se pueden añadir varios ficheros al mismo tiempo.

Page 7: Tema 12

4. Las referencias externas

Una referencia externa tiene la función de permitir que las funciones de un fichero puedan acceder a los datos y funciones de otro fichero fuente. Sin esta característica sería imposible utilizar varios ficheros fuente para un mismo programa.

La referencia a un elemento externo se compone de dos partes. Primero, el elemento debe estar correctamente definido en un fichero. Segundo, el elemento se debe declarar en cada uno de los ficheros que va a referenciarlo.

La palabra clave extern se utiliza en la declaración de aquellos elementos que se van a referenciar externamente. Esta palabra le indica al compilador que los datos que está declarando han sido definidos en otro fichero fuente. De esta forma, con extern, puede utilizar elementos que se han creado en otros ficheros fuente.

Por ejemplo: extern double ventas;declara un número en punto flotante de doble precisión. Esta declaración es diferente a otras anteriores porque la variable ventas ha sido definida en otro fichero fuente.

Page 8: Tema 12

4. Las referencias externas (cont.)

Declarar un elemento presenta una diferencia respecto a definirlo. La declaración le dice al compilador la clase de dato o elemento con el que va a trabajar. Sin embargo una declaración no reserva un espacio de memoria para el elemento. Una definición es un tipo de declaración especial que reserva memoria para el elemento declarado. Una definición permite crear un elemento, la declaración le da acceso a ese elemento.

Un elemento sólo puede definirse en un único fichero fuente. La definición del elemento produce que se asigne a ese elemento un determinado espacio de memoria. Una vez hecha la definición el elemento puede ser utilizado por cualquier fichero fuente.

double ventas;

...

int main () {

int i;

...

}

extern double ventas;

...

Calc () {

int i;

...

}

Page 9: Tema 12

4. Las referencias externas (cont.)

La variable i, declarada en cada uno de los ficheros fuente es única para dicho fichero. En otras palabras, la variable i de uno de los ficheros fuente no se puede referenciar dentro del otro fichero. De hecho, a la variable i sólo se la puede referenciar dentro de la función en la que se ha declarado, ya que i es una variable local. Esta variable se crea cuando comienza la ejecución de la función y cuando ésta termina de ejecutarse, se destruye la variable y la memoria que ocupaba se libera.

Las variables a las que necesitan acceder funciones de otros ficheros se declaran como variables globales (se definen fuera de todas las funciones de un fichero fuente). Para utilizar estas variables, declararlas simplemente utilizando el especificador de clase de almacenamiento extern en el nuevo fichero.

Page 10: Tema 12

5. Utilización de funciones externas

La palabra clave extern le hace saber al compilador que esa variable ha sido creada en otro fichero fuente.

Llamar a una función de otro fichero fuente es parecido a utilizar variables definidas en diferentes ficheros. Al utilizar las funciones externas se debe realizar dos pasos: primero definir la función en un fichero fuente y luego declararla en los otros ficheros.

No hay que hacer nada especial para definir una función que se pueda utilizar en varios ficheros fuente. Una función está disponible automáticamente para otros ficheros fuente. Lo único que no se debe olvidar es declarar la función en cada uno de los ficheros fuente que la vaya a utilizar.

La declaración de una función definida en otro fichero fuente no necesita el uso de la palabra clave extern como necesitaban las variables externas. Para utilizar una función de otro fichero fuente sólo hay que incluir el prototipo.

Page 11: Tema 12

6. Escritura de ficheros cabecera para módulos externos

Para utilizar variables y funciones que residan en otro fichero fuente se necesita declarar esas funciones y variables en nuestro fichero. Si se utilizan muchos ficheros fuente diferentes el teclear todas las declaraciones extra puede llegar a ser muy pesado. Con los ficheros cabecera se puede evitar todo este trabajo extra.

Un fichero de cabecera es un fichero especial que contiene todas las declaraciones que se necesitan para poder utilizar funciones y variables de otro fichero fuente específico. Cuando se quiera utilizar funciones, o variables, que se encuentren en uno de los otros ficheros fuente, simplemente se debe utilizar, la directiva #include para incluir las declaraciones dentro del fichero fuente actual.

Dos de las ventajas de utilizar ficheros cabecera son la velocidad y la prevención de errores. Incluir un fichero de cabecera es mucho más rápido que teclear las declaraciones necesarias para las funciones y variables de otro fichero. Los ficheros cabecera también previenen los errores porque únicamente hay que teclear la declaración una sola vez. Una vez creado el fichero de cabecera correctamente no hay que preocuparse de los errores causados al teclear.

Page 12: Tema 12

7. Qué incluir en un fichero de cabecera

El fichero de cabecera de un fichero fuente debe incluir todos aquellos elementos que se quieran referenciar externamente. Es decir, un fichero de cabecera debería contener las declaraciones de cualquier variable o función a la que se vaya a acceder desde otro fichero.

Al escribir en un fichero de cabecera las declaraciones de las variables que se van a poder utilizar desde otros ficheros, no hay que olvidar añadir el especificador de clase de almacenamiento extern en la declaración. Esto no es necesario en la declaración de las funciones.

Además de declaraciones de funciones y variables, un fichero de cabecera puede incluir definiciones de macros. Cuando el fichero de cabecera se incluye dentro de un fichero fuente podrá utilizar también todas las macros definidas en éste.

Para incluir nuestros propios ficheros cabecera, lo único que se necesita es utilizar la directiva #include.Por ejemplo: #include “prog1.h”

Page 13: Tema 12

7. Qué incluir en un fichero de cabecera (cont.)

Un fichero de cabecera puede incluir, a su vez, más directivas #include. Está completamente permitido anidar directivas #include.

Las comillas de la directiva include le indican al precompilador que se va a utilizar un fichero de cabecera del usuario. El precompilador busca primero en el directorio actual. Normalmente es ahí donde se localiza el fichero. Si, por el contrario, el fichero de cabecera no se encontrase ahí, lo buscará en los directorios donde busca los ficheros de cabecera de Borland C o de Dev C++.

Existe un compromiso entre el deseo de que cada archivo sólo tenga acceso a la información que necesita para su trabajo y la realidad práctica de que es más difícil mantener más archivos de cabecera. Hasta un tamaño moderado de programa, probablemente es mejor tener un archivo de cabecera que contenga todo lo que será compartido entre las partes del programa. Para un programa mucho más grande, se necesitaría más organización y más archivos de cabecera.

Page 14: Tema 12

8. Utilización de las directivas de compilación condicional en los ficheros de cabecera

Algunos programas a menudo necesitan incluir durante la compilación muchos ficheros de cabecera. Cuando se incluyen varios ficheros de cabecera puede ocurrir que se definan algunos elementos varias veces. Se pueden repetir declaraciones más de una vez sin que ocurra nada. De todas maneras, para una buena programación es mejor asegurarse que las declaraciones se realizan sólo una vez.

Las directivas de compilación condicional tienen como propósito incluir o excluir ciertas secciones de código antes de que tenga lugar la compilación. Utilizando la directiva del precompilador #if se puede decidir qué secciones de código van a ser incluidas o excluidas. Las diferentes formas de la directiva #if permiten realizar varias comprobaciones lógicas antes de que comience la compilación.

#if [!] expresión_constante#elif [!] expresión_constante#else#if [!] defined identificador

Page 15: Tema 12

8. Utilización de las directivas de compilación condicional en los ficheros de cabecera (cont.)

#if [!]defined (identificador)#ifdef identificador#ifndef identificador#endif

El símbolo [!] indica que se puede utilizar, opcionalmente, el operador lógico NOT en la expresión.

En la lista de las directivas de compilación condicional, expresión_constante puede ser cualquier expresión que se evalúe como un valor entero. Puede contener operadores aritméticos, lógicos y macros. Si al evaluarse toma un valor distinto de cero se considera verdadera, si toma el valor cero se considera falsa.

El identificador utilizado en la serie de directivas #if defined es cualquier símbolo creado por nosotros. Esta directiva determina si el símbolo indicado se ha definido previamente. El identificador puede estar, opcionalmente, encerrado entre paréntesis.

Page 16: Tema 12

8. Utilización de las directivas de compilación condicional en los ficheros de cabecera (cont.)

Con las directivas #if, #elif, #else y #endif se puede crear una construcción lógica que incluya o excluya una sección de código basándose en una cierta condición.

#if condicion_1<Primera sección de código>

#elif condicion_2<Segunda sección de código>

#else<Sección de código por defecto>

#endifCuando el precompilador lea el grupo de directivas #if anteriores lo primero que hará será evaluar la condicion_1. Si al evaluarla el resultado es un valor distinto de 0, se incluirá la primera sección de código en la compilación. Las otras secciones de código no se incluirán en la compilación.

Page 17: Tema 12

8. Utilización de las directivas de compilación condicional en los ficheros de cabecera (cont.)

Si condicion_1 produce el valor 0 el precompilador evaluará la condicion_2. La segunda sección de código se incluirá en la compilación del programa si condicion_2 resulta un valor distinto de 0. Si no son ciertas ni condicion_1 ni condicion_2, entonces se incluirá la sección de código por defecto que sigue a la directiva #else.No es necesario que se incluya código en alguna de estas secciones. A veces, se puede necesitar crear una estructura de compilación condicional pero no se quiere incluir código para alguna de las condiciones. La directiva #endif señala el final de la estructura #if. Se debe incluir una directiva #endif por cada #if que se utilice.

La directiva:#if !defined _PROG1#define _PROG1...#endif

hace que la sección de código que le sigue sea incluida únicamente si _PROG1 no se ha definido previamente. La primera acción que se realiza si _PROG1 no está definida es definirla. Una vez se haya definido, las declaraciones hechas en prog1.h no se repetirán si se incluye este fichero más de una vez.

Page 18: Tema 12

8. Utilización de las directivas de compilación condicional en los ficheros de cabecera (cont.)

Si se utilizan construcciones como la anterior nos aseguramos de que nuestros ficheros cabecera incluirán otros ficheros de cabecera una sola vez.

La directiva :#if defined _PROG1

es equivalente a la directiva:#ifdef _PROG1

Se puede utilizar #if defined en cualquier lugar donde se pueda utilizar #ifdef.

También son equivalentes las directivas:#if !defined _PROG1

con:#ifndef _PROG1

Page 19: Tema 12

9. Ejercicios

Veamos un ejemplo de aplicación distribuida en los siguientes ficheros:Archivo de cabecera:

Variable.hArchivos fuentes C:

Prog1.cProg2.cMain.c

Archivo de proyecto:Matriz.prj // en Borland C++Matriz.dev // en Dev-C++

Page 20: Tema 12

9. Ejercicios (cont.)

/* ________________ VARIABLE.H _________________ */#ifndef _VARIABLE#define _VARIABLE

#define MAX 3

extern int mat1[MAX][MAX];extern int mat2[MAX][MAX];extern int resul[MAX][MAX];

void sumar(void);void multiplicar(void);void invertir(void);void llenar_matriz(void);int menu(void);

#endif

Page 21: Tema 12

9. Ejercicios (cont.)

/*________________ PROG1.C _________________ */#include <stdio.h>#include <conio.h>#include "variable.h"void sumar(void){ int i,j; for (i=0;i<MAX;i++) for (j=0;j<MAX; j++){ resul[i][j]=mat1[i][j]+mat2[i][j]; }for (i=0;i<MAX;i++) for (j=0;j<MAX; j++){ gotoxy(j*3+60,i+2);printf("%d",resul[i][j]); }}

Page 22: Tema 12

9. Ejercicios (cont.)

void multiplicar(void){

/* Algoritmo para multiplicar matrices */

}

void invertir(void){

/* Algoritmo para invertir una matriz */

}

Page 23: Tema 12

9. Ejercicios (cont.)

/* _______________ PROG2.C___________ */#include <stdio.h>#include <conio.h>#include "variable.h"int mat1[MAX][MAX],mat2[MAX][MAX],resul[MAX][MAX];void llenar_matriz(void){ int i,j; clrscr();

printf ( "MATRIZ 1");

for(i =0;i<MAX;i++) for (j=0;j<MAX;j++){ gotoxy( j*3+1, i+2); scanf ("%d",&mat1[i][j]); fflush(stdin); }

Page 24: Tema 12

9. Ejercicios (cont.)

gotoxy(30,1); printf ( "MATRIZ 2");

for(i =0;i<MAX;i++) for (j=0;j<MAX;j++){ gotoxy( j*3+30, i+2); scanf ("%d",&mat2[i][j]); fflush(stdin); }}

Page 25: Tema 12

9. Ejercicios (cont.)

int menu(void){ int opcion;

gotoxy(20,10);printf("1.- SUMAR"); gotoxy(20,12);printf("2.- MULTIPLICAR"); gotoxy(20,14);printf("3.- INVERTIR"); gotoxy(20,16);printf("4.- Salir"); gotoxy(60,22);printf( " ELIGE OPCION "); scanf("%d",&opcion);

while (opcion<1 || opcion>4){ gotoxy(60,22);printf( " ELIGE OPCION "); scanf("%d",&opcion); } return(opcion);}

Page 26: Tema 12

9. Ejercicios (cont.)

/* ________________ MAIN.C ___________ */#include <stdio.h>#include <conio.h>#include "variable.h"int main(){ int op; llenar_matriz(); while ((op=menu())>0 && op<4){ switch(op){ case 1: sumar();

break; case 2: multiplicar();

break; case 3: invertir();

break; } return 0; }

Page 27: Tema 12

9. Ejercicios (cont.)

clrscr();

gotoxy(37,12);

printf ("F I N");

getch();

}