Sei sulla pagina 1di 68

DIAPOSITIVAS DE "PROGRAMACIN"

GRADO EN INGENIERA EN TECNOLOGAS DE LA TELECOMUNICACIN GRADO EN INGENIERA EN SISTEMAS DE TELECOMUNICACIN GRADO EN INGENIERA ELECTRNICA DE COMUNICACIONES GRADO EN INGENIERA TELEMTICA

Escuela Politcnica Superior Universidad de Alcal

NDICE
1. REPASO DE PUNTEROS
CONCEPTOS FUNDAMENTALES ERRORES TPICOS EN EL USO DE PUNTEROS OPERACIONES CON PUNTEROS PRIORIDADES CON LOS OPERADORES ++ Y -PUNTEROS GENRICOS PUNTEROS Y ARRAYS DE DATOS PUNTEROS A CADENAS DE CARACTERES

2. REPASO DE PUNTEROS (II)


ARRAYS DE PUNTEROS PUNTEROS A PUNTEROS PUNTEROS A ESTRUCTURAS Y UNIONES ASIGNACIN DINMICA DE MEMORIA

3. MS SOBRE PUNTEROS
PASO DE ARGUMENTOS POR REFERENCIA PASO DE UN ARRAY UNIDIMENSIONAL COMO ARGUMENTO A UNA FUNCIN PASO DE UN ARRAY BIDIMENSIONAL COMO ARGUMENTO A UNA FUNCIN PASO DE UN PUNTERO COMO ARGUMENTO A UNA FUNCIN DATOS RETORNADOS POR UNA FUNCIN

4. PASO DE ESTRUCTURAS A FUNCIONES, PUNTEROS A FUNCIONES


PASO DE ESTRUCTURAS Y UNIONES POR VALOR PASO DE ESTRUCTURAS Y UNIONES POR REFERENCIA PUNTEROS A FUNCIONES

5. RECURSIVIDAD, ARGUMENTOS EN LNEA DE RDENES


FUNCIONES RECURSIVAS ARGUMENTOS EN LNEA DE RDENES

6. MANEJO BSICO DE FICHEROS


CONCEPTOS FUNDAMENTALES SOBRE FICHEROS FLUJO ASOCIADO A UN FICHERO APERTURA DE UN FICHERO CIERRE DE UN FICHERO

DETECCIN DE ERRORES DE ACCESO A UN FICHERO DETECCIN DE FIN DE FICHERO ANULACIN DE ERRORES DE ACCESO A FICHERO IMPRIMIR EN PANTALLA MENSAJES DE ERROR POSICIN DEL APUNTADOR DE LECTURA/ESCRITURA CONTROL DEL BUFFER DEL FICHERO

7. LECTURA/GRABACIN DE DATOS EN FICHEROS


LECTURA Y GRABACIN DE DATOS CARCTER A CARCTER GRABACIN DE CADENAS DE CARACTERES LECTURA DE CADENAS DE CARACTERES LECTURA Y GRABACIN DE DATOS CON FORMATO GRABACIN DE REGISTROS LECTURA DE REGISTROS

8. MS SOBRE FICHEROS
FICHEROS TEMPORALES ELIMINACION DE FICHEROS CAMBIAR EL NOMBRE DE FICHEROS COPIAR FICHEROS ESCRIBIR DATOS EN LA IMPRESORA VER DIRECTORIOS DE FICHEROS ACCESO SECUENCIAL A FICHEROS

9. ACCESO ALEATORIO A FICHEROS


ACCESO ALEATORIO A FICHEROS EJEMPLO PRCTICO

10. ESTRUCTURAS DINMICAS


CONCEPTOS GENERALES LISTAS LINEALES CREACIN DE UNA LISTA LINEAL SIMPLEMENTE ENLAZADA INSERCIN DE UN ELEMENTO EN MITAD DE LA LISTA ELIMINACIN DE ELEMENTOS DE LA LISTA BSQUEDA DE ELEMENTOS EN LA LISTA

11. ESTRUCTURAS DINMICAS (II)


PILAS COLAS LISTAS CIRCULARES LISTAS DOBLEMENTE ENLAZADAS

12. RBOLES BINARIOS


CONCEPTOS GENERALES RBOLES BINARIOS RBOLES BINARIOS DE BSQUEDA RECORRIDO DE RBOLES BINARIOS BSQUEDA EN RBOLES BINARIOS DE BSQUEDA

13. LGORITMOS DE ORDENACIN DE DATOS


CONCEPTOS GENERALES MTODO DE LA BURBUJA MTODO DE SELECCIN MTODO DE INSERCIN MTODO RECURSIVO QUICKSORT COMPARATIVA DE ALGORITMOS

14. LGORITMOS DE BSQUEDA DE DATOS


BSQUEDA SECUENCIAL BSQUEDA BINARIA ALGORITMOS HASH PROCEDIMIENTO DE ALMACENAMIENTO HASH PROCEDIMIENTO DE BSQUEDA HASH PROCEDIMIENTO DE BORRADO HASH

1. REPASO DE PUNTEROS
CONCEPTOS FUNDAMENTALES: Un puntero es una variable que contiene en su interior una direccin de memoria donde reside un dato. Este dato apuntado por un puntero puede ser de cualquier tipo bsico (char, short, int, long, float, double) o derivado (array, struct, union,). El tamao en bytes de un puntero es fijo (2 o 4 bytes, dependiendo del compilador y del ordenador utilizados), independientemente del tipo de dato apuntado. Supondremos 4 bytes. Una variable puntero se declara as: TipoApuntado *NombrePuntero; Ejemplos: int *p; Puntero a dato int double *q; Puntero a dato double Una vez declarado, hay que cargarlo con la direccin de algn dato existente. Mientras no est as inicializado, el puntero no puede ser utilizado. Los operadores principales utilizados con punteros son dos: & (direccin de) y * (lo apuntado por, operador de indireccin). Ejemplos: int n=10, m; Variables n y m de tipo int, n inicializada con el valor 10 int *p; Puntero a dato int, puntero an no inicializado p = &n; El puntero se carga con la direccin de la variable n m = *p; La variable m se carga con lo apuntado por p (m=n) (m=10) ERRORES TPICOS EN EL USO DE PUNTEROS: Usar el operador indireccin (*) cuando el puntero no est an correctamente inicializado (no sabemos dnde apunta), o bien est cargado con el valor NULL. NULL es un puntero de valor 0 (constante simblica importante del compilador C). Ejemplo: main() { int n=10, m; int *p=&n, *q, *r=NULL; m=*p; Bien, m copia el valor de n m=*q; Mal, no sabemos dnde apunta q m=*r; Mal, r vale NULL, no se puede almacenar nada en esta direccin } Cargar un puntero de un tipo con la direccin de un dato de otro tipo. Ejemplo: main() { double a=10.33, b; Dos variables de tipo double int *p; Puntero a dato int p=&a; p se carga con la direccin de a, el compilador genera mensaje de error b=*p; b toma un valor impredecible (no 10.33), porque p es un puntero a int } OPERACIONES CON PUNTEROS: Copiar un puntero en otro. Ejemplo: main() { int a=10, *p, *q; p = &a; q = p; Sumar o restar a un puntero un nmero entero (til slo cuando apunta a un array de datos). Ejemplo: main() { int x[100], *p=&x[3]; p = p+3; p apuntar a x[6] p = p-2; p apuntar a x[4]

Incrementar (++) o decrementar (--) un puntero (til slo cuando apunta a un array de datos). Ejemplo: main() { int x[100], *p=&x[3]; p++; ++p; p apuntar a x[5] ambos casos equivalen a p = p+1; p--; --p; p volver a apuntar a x[3] ambos casos equivalen a p = p-1; Restar entre s dos punteros de un mismo tipo (til slo cuando apuntan a un array de datos). Ejemplo: main() { int x[100], n; int *p=&x[3], *q=&x[0]; n = p-q; n valdr 3 (distancia en datos int entre p y q) n = q-p; n valdr -3 (distancia en datos int entre q y p) Para saber la distancia en n de bytes entre p y q: n = (char *)p (char *)q; n valdr 3x4=12 (suponiendo datos int de 4 bytes) Comparar entre s dos punteros (til slo cuando apuntan a un array de datos). Ejemplo: main() { int x[100], n, *p=&x[3], *q=&x[0]; .. if (p>q) { .. } if (p-n >= q+2) { .. } if (q!=NULL && q<=p) { .. }
EJEMPLO-01. // OPERACIONES CON PUNTEROS #include <stdio.h> main() { int x[10]={10,20,30,40,50,60,70}, b, *pa, *pb; pa = &x[5]; //pa apunta a x[5]=60 b = *pa + 1; //b=61 b = *(pa + 1); //b=70 pb = &x[3]; //pb apunta a x[3]=40 b = pb-pa; //b=-2 *pb = 0; //x[3]=0 *pb += 2; //x[3] = x[3]+2 ===> x[3]=2 (*pb)--; //x[3]-- ===> x[3]=1 x[0] = *pb--; //x[0]=x[3] y luego pb se decrementa, apuntar a x[2] x[2] = *--pb; //primero se decrementa pb (apuntar a x[1]), luego x[2]=*pb, o sea, x[2]=x[1] if (pb > pa-5) //condicin cierta: pb apunta a x[1], pa-5 apunta a x[0] b = 0; //b se pone a 0 }

PRIORIDADES CON LOS OPERADORES ++ Y --: Los operadores de incremento (++) y decremento (--) de tipo prefijo tienen la misma prioridad alta que los operadores "direccin de" (&) e indireccin (*). Si hay varios de estos operadores en la misma expresin, se evalan de derecha a izquierda. Los operadores ++ y -- de tipo sufijo tienen muy baja prioridad, y se evalan al final del todo, incluso despus del operador de asignacin (=). Los 4 posibles casos de convivencia del operador indireccin (*) e incremento/decremento de alta o baja prioridad son los siguientes: main() { int c, *p; c = *p++; equivale a c = *p; p++; o bien c = *p; p = p+1; c = *++p; equivale a ++p; c = *p; o bien p = p+1; c = *p; c = ++*p; equivale a *p+=1; c = *p; o bien *p = *p+1; c = *p;

c = (*p)++; equivale a

c = *p; *p+=1;

o bien

c = *p; *p = *p+1;

PUNTEROS GENRICOS: Un puntero "genrico" es aquel que puede apuntar a diversos tipos de datos a lo largo de la ejecucin del programa. Se declara de la siguiente forma: void *NombrePuntero; Una vez declarado, se puede copiar en l cualquier tipo de direccin, con un simple operador de asignacin (=): NombrePuntero = &dato; Una vez cargado, para acceder al dato apuntado, hay que utilizar el operador de "indireccin" (*), pero haciendo uso de un operador "cast" de conversin explcita de tipo para punteros, que indique el tipo de dato que est siendo apuntado actualmente por el puntero: n = *(tipoDato *)NombrePuntero;
EJEMPLO-02. // PUNTEROS GENRICOS #include <stdio.h> main() { int dato1 = 10, dato2; //datos int float dato3 = 3.14, dato4; //datos float void *punt; //puntero genrico float *p = &dato3; //...... punt = &dato1; //punt apunta a dato1, de tipo int dato2 = *(int *)punt; //accedemos a dato1 a travs de punt, lo copiamos en dato2 //...... punt = &dato3; //punt apunta a dato3, de tipo float dato4 = *(float *)punt; //accedemos a dato3 a travs de punt, lo copiamos en dato4 //...... punt = p; //bien, siempre se puede asignar a un puntero genrico otro puntero p = punt; //no siempre bien, depende del compilador, puede generar error p = (float *)punt; //siempre bien, haciendo uso de un operador cast }

PUNTEROS Y ARRAYS DE DATOS: Con un puntero se puede manipular cmodamente un array de datos, accediendo a todos sus elementos. Hay varias sintaxis posibles para conseguirlo, pero la ms cmoda es aplicar al puntero por su derecha un subndice de tipo int metido entre corchetes, igual que haramos con un array.
EJEMPLO-03. // PUNTEROS Y ARRAYS DE DATOS #include <stdio.h> main() { int lista[] = {24,30,15,25,18}; //Array de 5 datos de tipo int int i; //Variable para controlar el subndice del array int *p = &lista[0]; //Puntero para manipular el array (tambin habra servido int *p=lista; ) for (i=0; i<5; i++) //Bucle de exploracin SIN puntero, con 2 posibilidades sintcticas printf("%d ", lista[i]); //Esta es la sintaxis ms cmoda printf("%d ", *(lista+i)); //Sintaxis aceptable, el valor de "lista" no se modifica printf("%d ", *lista++); //Sintaxis NO aceptable, pretende alterar el valor de "lista" for (i=0; i<5; i++) printf("%d ", *(p+i)); printf("%d ", *p++); printf("%d ", p[i]); corchetes } //Bucle de exploracin CON puntero, con 3 posibilidades sintcticas //Aqu el puntero se mueve para ir apuntando a los sucesivos datos //Esta es la sintaxis ms cmoda, idntica al uso del array con

A todo puntero p se le puede aplicar un subndice i entre corchetes, el resultado es siempre acceder al dato colocado i posiciones por encima de la apuntada por p: p[i] *(p+i) PUNTEROS A CADENAS DE CARACTERES: Se pueden declarar variables puntero para manipular arrays de datos de tipo char, o sea, cadenas de caracteres. Estos punteros son modificables, y pueden pasar a apuntar a otras cadenas utilizando un sencillo operador de asignacin (=), cosa que no se poda hacer con los arrays.
EJEMPLO-04. // PUNTEROS A CADENAS #include <stdio.h> main() { char cad[40] = "Hola"; //cad es un array de datos char, el nombre "cad" es su direccin de inicio char *pc = cad; //pc es un puntero variable, apuntando al inicio de cad printf("%s", cad); //Imprime Hola printf("%s", pc); //Imprime Hola pc = "Adios"; //Bien, el puntero a variado y apunta al comienzo de la constante Adios printf("%s", pc); //Imprime Adios cad = "Adios"; //MAL, cad no se puede cambiar con el operador de asignacin (=) strcpy(cad,"Adios"); //Bien, hemos copiado en el array cad un nuevo contenido printf("%s", cad); //Imprime Adios printf("%c", pc[1]); //Imprime d printf("%c", cad[1]); //Imprime d pc++; //pc apunta a la d printf("%s", pc); //Imprime dios }

2. REPASO DE PUNTEROS (II)


ARRAYS DE PUNTEROS: Se puede declarar un array compuesto por elementos de tipo puntero, de la siguiente forma: TipoDatoApuntado *NombreArray[NumElementos]; El nombre del array representa la direccin de memoria de comienzo del array. Estos arrays de punteros se utilizan principalmente para manipular cmodamente un array de datos de dos dimensiones.
EJEMPLO-01. // ARRAY DE PUNTEROS #include <stdio.h> main() { float a[5][4]; //array de datos bidimensional a controlar float *p[5]; //array de punteros asociado int i,j; //indices para bucles for (i=0; i<5; i++) //bucle para conectar los punteros con las filas del array de datos p[i] = &a[i][0]; //tambin servira p[i]=a[i]; printf("Introduce los datos del array...\n"); for (i=0; i<5; i++) //bucle SIN hacer uso del array de punteros for (j=0; j<4; j++) scanf("%f", &a[i][j]); //con punteros sera scanf("%f", &p[i][j]); printf("\nLos datos introducidos son...\n"); for (i=0; i<5; i++) //bucle CON utilizacin del array de punteros { for (j=0; j<4; j++) printf("%g\t", p[i][j]); //otra posible sintaxis de p[i][j] es: *(*(p+i)+j) printf("\n"); } }

PUNTEROS A PUNTEROS: Se puede declarar un puntero que apunte a otro puntero, de la siguiente forma: TipoDatoApuntado **NombrePuntero; De esta forma, podemos manipular el puntero simple apuntado, aplicando una indireccin (*) al puntero doble, y podemos manipular el dato final apuntado aplicando una doble indireccin (**) al puntero doble. Ejemplos: main() { int n = 10, m; dos variables de tipo int int *p = &n; un puntero simple apuntando a n int *pp = &p; un puntero doble (puntero a puntero) apuntando a p m = *p; accedemos a n a travs de p, m=10 m = **pp; de nuevo accedemos a n a travs de pp (doble indireccin), m=10 *pp = &m; hacemos que p pase a apuntar a m, lo mismo que p=&m; Estos punteros se suelen usar para manipular cmodamente un array de punteros simples, que, como hemos visto, sirve a su vez para manipular un array de datos de dos dimensiones. Ejemplo: main() { char *pc[] = {"Jose", "Ana", "Alberto", "Sonia"}; char **q = pc; printf("%s", q[0]); Imprime "Jose", igual que: printf("%s", pc[0]);

printf("%c", q[3][0]);

Imprime "S", igual que: printf("%c", pc[3][0]);

PUNTEROS A ESTRUCTURAS Y UNIONES: Se puede declarar un puntero que apunte a una variable de tipo estructura (struct) o de tipo union. Para acceder a los campos de dichas estructuras/uniones a travs del puntero, se utiliza el operador "flecha" (->), ya no el operador "punto" (.) que se utiliza con el nombre de la variable struct. Este tipo de acceso mediante puntero es el nico modo posible cuando creamos variables de tipo struct/union mediante asignacin dinmica de memoria.
EJEMPLO-02. // PUNTEROS A ESTRUCTURAS #include <stdio.h> main() { struct fecha { unsigned int dd,mm,aa; }; //declaracin del tipo struct struct fecha f = {25,12,2010}; //declaracin de la variable struct, e inicializacin de sus campos struct fecha *p = &f; //declaracin de un puntero p que apunta a f printf("La fecha es: %d-%d-%d", f.dd, f.mm, f.aa); //impresin a travs de la variable struct printf("\nLa fecha es: %d-%d-%d", p->dd, p->mm, p->aa); //impresin a travs del puntero }

ASIGNACIN DINMICA DE MEMORIA: Esta tcnica permite crear nuevas variables de memoria o arrays de cualquier tipo en tiempo de ejecucin del programa, sin necesidad de haberlas declarado de antemano en el cdigo fuente. Slo es necesario declarar una variable puntero para cada nueva variable o array de datos que queramos crear. Esto se consigue principalmente con las funciones malloc y free: #include <stdlib.h> void *malloc(unsigned int Numbytes); void free(void *Puntero); La funcin malloc recibe como argumento el nmero de bytes consecutivos que queremos reservar para nuestra nueva variable, realiza la bsqueda de tal espacio libre de la memoria, lo reserva y devuelve la direccin de memoria de comienzo del mismo. Esta direccin debe ser almacenada en una variable del tipo puntero adecuado (el tipo de dato de nuestra variable recin creada), y a travs de tal puntero manipularemos la nueva variable. Esta zona de memoria permanece reservada hasta que es liberada mediante la funcin free, que recibe como argumento dicho puntero que la apunta. Si tal zona no es liberada mediante free, puede quedar reservada incluso ms all de la finalizacin del programa, generando "lagunas de memoria" que no pueden ser utilizadas por otras aplicaciones, hasta que el ordenador es reseteado.
EJEMPLO-03. // ASIGNACION DINMICA DE MEMORIA #include <stdio.h> #include <stdlib.h> main() { float *p; //crearemos una variable de datos de tipo float struct fecha { int dd,mm,aa; } *q; //crearemos tambin una variable estructura p = (float *)malloc(4); //reservamos memoria para la variable float if (p==NULL) { printf("Error: No hay memoria suficiente."); exit(0); }

q = (struct fecha *)malloc(sizeof(struct fecha)); //reservamos memoria para la variable struct if (q==NULL) { printf("Error: No hay memoria suficiente."); free(p); exit(0); } printf("Introduzca la variable float:"); scanf("%f", p); //Introducimos por teclado la variable float printf("Introduzca dia, mes y ao:"); scanf("%d %d %d", &q->dd, &q->mm, &q->aa); //Introducimos por teclado la variable struct free(p); free(q); //liberamos la memoria antes de terminar el programa }

Si con la funcin malloc reservamos espacio para varios datos sucesivos en la memoria, estaremos creando un "array dinmico", cuyo tamao puede ser variado a lo largo de la ejecucin del programa. Tal array ser manipulado con facilidad mediante el puntero mencionado anteriormente.
EJEMPLO-04. // ARRAY DINAMICO #include <stdio.h> #include <stdlib.h> main() { float *p; //crearemos un array dinmico de datos float int numelem=0, i; do { printf("Dime N de elementos del array a crear:"); scanf("%d", &numelem); //pedimos por teclado el n de elementos deseado } while (numelem<1); p = (float *)malloc(numelem*4); //reservamos memoria para el array dinmico if (p==NULL) { printf("Error: No hay memoria suficiente."); exit(0); } printf("Introduzca los datos del array:\n"); for (i=0; i<numelem; i++) //Introducimos por teclado los datos del array scanf("%f", &p[i]); printf("\nLos datos introducidos son:\n"); for (i=0; i<numelem; i++) //Imprimimos en pantalla los datos del array printf("%g", p[i]); free(p); } //liberamos la memoria antes de terminar el programa

Existe la funcin especfica calloc para creacin de arrays dinmicos, y la funcin realloc para modificacin del tamao en bytes de un array dinmico: #include <stdlib.h> void *calloc(unsigned int NumElementos, unsigned int TamElemento); void *realloc(void *Puntero, unsigned int Numbytes); La funcin calloc recibe dos argumentos, el n de elementos del array a crear, y el tamao en bytes de uno de esos elementos. Reserva tantos bytes sucesivos como el producto de ambos argumentos, y adems pone a ceros la zona de memoria recin reservada (cosa que no hace malloc). La funcin realloc recibe dos argumentos, el puntero que apunta a la zona de memoria reservada ya existente, y el nuevo tamao en n de bytes deseado para tal zona. Los datos ya almacenados en la zona inicial no se pierden. Si el primer argumento (el puntero) vale NULL, realloc se comporta igual que malloc.

EJEMPLO-05. // REALLOC // Toma de datos en un array dinmico de tamao creciente #include <stdio.h> #include <stdlib.h> main() { int *p=NULL; //puntero maestro del array dinmico int *paux; //puntero auxiliar int i=0; //indice para bucle int num=0; //N de datos actuales del array int dato; //aqu leemos el dato desde el teclado printf("Dame datos del array (Ctrl+Z = Fin):"); while (scanf("%d", &dato) != EOF) //leemos dato por teclado y comprobamos Ctrl+Z { num++; //incrementar n de elementos del array paux = (int *)realloc(p, num*sizeof(int)); //ampliar espacio para el array if (paux==NULL) //No hay memoria { num--; //decrementar n de elementos del array printf("Memoria llena"); } else { p=paux; //actualizar puntero maestro p p[num]=dato; //copiar dato al array dinmico } } printf("\nLos datos introducidos son:\n"); for (i=0; i<num; i++) { printf("%d ", p[i]); } free(p); //liberar memoria antes de terminar el programa }

3. MS SOBRE PUNTEROS
PASO DE ARGUMENTOS POR REFERENCIA: Un argumento de entrada a una funcin es pasado "por referencia" cuando a la funcin se le pasa la direccin de memoria donde reside dicho argumento, haciendo uso del operador "direccin de" (&). Esta direccin debe ser recibida en la cabecera de la funcin mediante una variable de tipo puntero. Este tipo de paso de argumentos permite a la funcin acceder a la misma variable que ha sido pasada como argumento, y no a una copia de dicha variable (tal como sucede en el paso de argumentos "por valor"). Dicho acceso a la variable se hace a travs del puntero recibido, con el operador indireccin (*). En el paso de argumento "por valor", la funcin recibe una copia de la variable original, y no hay acceso posible a dicha variable original. Todo tipo de variable puede ser pasada como argumento en las dos formas, por valor y por referencia, excepto los arrays, que nicamente pueden ser pasados por referencia (siempre se pasa la direccin de comienzo del array).
EJEMPLO-01. // PASO DE ARGUMENTOS POR REFERENCIA Y POR VALOR // Programa para intercambiar los valores de dos variables #include <stdio.h> void intercambio1(int x, int y); void intercambio2(int *x, int *y); main() { int a=10, b=20; //queremos intercambiar a con b intercambio1(a,b); //paso de argumentos por valor printf("a=%d, b=%d",a,b); //imprime a=10, b=20 No se ha producido el intercambio intercambio2(&a, &b); //paso de argumentos por referencia printf("a=%d, b=%d",a,b); //imprime a=20, b=10 S se ha producido el intercambio } void intercambio1(int x, int y) //argumentos recibidos por valor, "x e y" son copias de "a y b" { int z; //variable intermedia necesaria en todo intercambio z = x; x = y; y = z; } void intercambio2(int *x, int *y) //argumentos recibidos por referencia, "x e y" apuntan a "a y b" { int z; //variable intermedia necesaria en todo intercambio z = *x; *x = *y; *y = z; }

PASO DE UN ARRAY UNIDIMENSIONAL COMO ARGUMENTO A UNA FUNCIN: Un array unidimensional siempre es pasado "por referencia" como argumento a una funcin (nunca por valor), aportando en la llamada a la funcin el nombre del array, nombre que representa la direccin de inicio del array. Esta direccin puede ser recibida en la cabecera de la funcin con dos posibles sintaxis: tipo array (con corchetes []) o tipo puntero (con asterisco *). En el primer caso (con corchetes), la variable receptora en la cabecera de la funcin acta como un puntero constante que no puede ser modificado. En el segundo caso (con asterisco), la variable receptora acta como un puntero variable que s puede ser modificado, lo cual le aporta mayores posibilidades.

El paso del nombre del array como argumento slo informa sobre su direccin de comienzo, pero no sobre su tamao en n de elementos. Habitualmente hay que pasar tambin esta informacin a la funcin, mediante un segundo argumento de entrada de tipo int.
EJEMPLO-02. // PASO DE ARRAYS UNIDIMENSIONALES COMO ARGUMENTOS A FUNCIONES // Programa para copiar un array en otro #include <stdio.h> void CopiaArray1(int x[], int y[], int num); void CopiaArray2(int *x, int *y, int num); void CopiaArray3(int *x, int *y, int num); main() { int a[5]={24,30,15,25,18}; //Array de origen de la copia int b[5]; //Array de destino de la copia int numelem = sizeof(a)/sizeof(int); //n de elementos del array a int i; CopiaArray1(a, b, numelem); //las tres funciones hacen lo mismo CopiaArray2(a, b, numelem); //de tres maneras diferentes CopiaArray3(a, b, numelem); printf("Los datos copiados en el array b son: "); for (i=0; i<numelem; i++) printf("%d ",b[i]); } void CopiaArray1(int x[], int y[], int num) //arrays recibidos con sintaxis de array [] { int i; for (i=0; i<num; i++) y[i] = x[i]; } void CopiaArray2(int *x, int *y, int num) //arrays recibidos con sintaxis de puntero * { int i; for (i=0; i<num; i++) y[i] = x[i]; } void CopiaArray3(int *x, int *y, int num) //arrays recibidos con sintaxis de puntero * { while (num) { *y++ = *x++; num--; } }

PASO DE UN ARRAY BIDIMENSIONAL COMO ARGUMENTO A UNA FUNCIN: Un array bidimensional o multidimensional siempre es pasado "por referencia" como argumento a una funcin, aportando en la llamada a la funcin el nombre del array, que representa su direccin de inicio. Esta direccin puede ser recibida en la cabecera de la funcin con sintaxis tipo array (con corchetes []) o tipo puntero (con asterisco *). En el primer caso, se debe dejar en blanco la primera dimensin, y todas las dems sucesivas deben ser indicadas con valores constantes (nmeros enteros o constantes simblicas). El paso del nombre del array como argumento slo informa sobre su direccin de comienzo, pero no sobre su tamao en n de elementos de sus varias dimensiones. Esta informacin debe ser suministrada tambin a la funcin, mediante otros argumentos de entrada.
EJEMPLO-03. // PASO DE ARRAYS BIDIMENSIONALES COMO ARGUMENTOS A FUNCIONES // Programa para copiar un array bidimensional en otro #include <stdio.h>

#define FILAS 3 #define COLS 2 void CopiaArray1(int x[][COLS], int y[][COLS]); void CopiaArray2(int *x, int *y); main() { int a[FILAS][COLS]={24,30,15,25,18,13}; //Array de origen de la copia int b[FILAS][COLS]; //Array de destino de la copia int i,j; CopiaArray1(a, b); //ambas funciones hacen lo mismo CopiaArray2(a, b); //de dos maneras diferentes printf("Los datos copiados en el array b son: "); for (i=0; i<FILAS; i++) for (j=0; j<COLS; j++) printf("%d ",b[i][j]); } void CopiaArray1(int x[][COLS], int y[][COLS]) //arrays recibidos con sintaxis de array [] { int i,j; for (i=0; i<FILAS; i++) for (j=0; j<COLS; j++) y[i][j] = x[i][j]; } void CopiaArray2(int *x, int *y) //arrays recibidos con sintaxis de puntero * { int i,j; for (i=0; i<FILAS; i++) for (j=0; j<COLS; j++) y[i*COLS+j] = x[i*COLS+j]; //o bien: *y++ = *x++; //NO se puede hacer: y[i][j] = x[i][j]; }

PASO DE UN PUNTERO COMO ARGUMENTO A UNA FUNCIN: Una variable de tipo puntero puede ser pasada como argumento a una funcin bien "por valor" o bien "por referencia". Si se pasa por valor, la cabecera de la funcin recibe en una variable de tipo puntero una copia del puntero original pasado en la llamada, y no hay acceso posible al puntero original. Si se pasa por referencia, en la llamada a la funcin se aplica el operador "direccin de" (&) al puntero que deseamos pasar, y en la cabecera de la funcin se recibe tal direccin en una variable de tipo "puntero a puntero" (**). Mediante este puntero podemos acceder directamente al puntero original, no a una copia del mismo.
EJEMPLO-04. // PASO DE PUNTEROS COMO ARGUMENTOS A FUNCIONES // Programa para encontrar el dato mayor de un array #include <stdio.h> int *buscaMayor1(int *x, int numelem); void buscaMayor2(int *x, int numelem, int **pp); main() { int a[]={24,30,15,25,38,23}; int *p; //aqu se guardar la direccin del elemento mayor del array p = buscaMayor1(a, 6); //ambas funciones hacen lo mismo buscaMayor2(a, 6, &p); //de dos maneras diferentes printf("El dato mayor es: %d\n", *p);

printf("Se encuentra en la posicion del array: %d", p-a); } int *buscaMayor1(int *x, int numelem) { int i; int posMayor=0; //indica el ndice del elemento mayor encontrado for (i=1; i<numelem; i++) if (x[i]>x[posMayor]) posMayor = i; return x+i; //retorna la direccin del elemento i-simo del array } void buscaMayor2(int *x, int numelem, int **pp) //recibe el puntero p por referencia { int i, posMayor=0; for (i=1; i<numelem; i++) if (x[i]>x[posMayor]) posMayor = i; *pp = x+i; //guarda directamente en el puntero p de main() la direccin buscada }

DATOS RETORNADOS POR UNA FUNCIN: Una funcin puede retornar, mediante la sentencia "return xxx;", un nico resultado a la funcin que la invoc, o bien no retornar ninguno (no existe sentencia return, o bien es "return;"). Si se retorna un resultado, este es normalmente un valor copia de alguna variable local de la funcin, variable local que desaparecer de la memoria una vez concluida la funcin y devuelta la copia de resultado. Tambin una funcin puede devolver como resultado una direccin de memoria, pero en este caso debemos asegurarnos que sea la direccin de una variable que permanezca en la memoria despus de la finalizacin de la funcin (que no sea de una variable local normal). Esto se puede conseguir devolviendo la direccin de una variable local "static" (que permanece en la memoria durante toda la ejecucin del programa), o bien de una variable creada con asignacin dinmica de memoria con la funcin "malloc"(que permanece en la memoria mientras no sea liberada con la funcin free), o bien la direccin de una constante de cadena de caracteres (que siempre est en la memoria formando parte del programa en ejecucin).
EJEMPLO-05. // FUNCIN QUE RETORNA UNA DIRECCIN // Funcin para devolver un mensaje habitual dependiendo de un cdigo numrico #include <stdio.h> char *msg(int cod); main() { int i; printf("Vamos a imprimir los 5 mensajes ms habituales de mis programas:\n\n"); for (i=0; i<5; i++) printf("Mensaje %d: %s\n", i, msg(i)); } char *msg(int cod) { static char txt[5][60] = {"Pulse una tecla para continuar...", "Procesando datos, espere...", "Espere, por favor...", //txt es un array local "static" "Pulse ESC para abandonar...", "Pulse una tecla para terminar."}; return txt[cod]; //retorna la direccin de comienzo de la fila del array dada por "cod" }

4. PASO DE ESTRUCTURAS A FUNCIONES, PUNTEROS A FUNCIONES


PASO DE ESTRUCTURAS Y UNIONES POR VALOR: Una variable de tipo estructura o unin es pasada normalmente "por valor" como argumento de entrada a una funcin, es decir, que la funcin recibe una copia de dicha variable y trabaja con dicha copia. La variable original no puede ser alcanzada desde dentro de la funcin. Dentro de la funcin se utiliza el operador "punto" (.) para alcanzar los campos internos de la variable estructura/unin de copia recibida (nombreVariable.nombreCampo). PASO DE ESTRUCTURAS Y UNIONES POR REFERENCIA: Se puede pasar una variable de tipo estructura o unin como argumento de entrada a una funcin tambin "por referencia", es decir, que la funcin recibe la direccin de memoria donde reside la variable original. En la llamada a la funcin se utiliza el operador "direccin de" (&) para pasar la direccin de la variable estructura/unin. En la cabecera de la funcin se recibe dicha direccin en una variable de tipo puntero a estructura/unin. La variable original s puede ser alcanzada desde dentro de la funcin, a travs de dicho puntero. Dentro de la funcin se utiliza el operador "flecha" (->) para alcanzar los campos internos de la variable estructura/unin original (nombrePuntero->nombreCampo).
EJEMPLO-01. // PASO DE ESTRUCTURAS POR VALOR Y POR REFERENCIA // Programa para cargar desde teclado datos de consumo telefnico mensual #include <stdio.h> #define MAX 50 //n de elementos del array de estructuras struct datotfno //declaracin del tipo estructura { int mes,anyo; float euros; }; void introducir(struct datotfno *tf); //declaraciones prototipo de funciones auxiliares void imprimir(struct datotfno tf); main() { int n,i; struct datotfno tfno[MAX]; //array de estructuras system("cls"); //borrar pantalla, en Unix es: system("clear"); printf("Introduzca los datos de consumo telefnico:\n\n"); for (i=0; i<MAX; i++) //bucle para introduccin de registros { printf("\nRegistro N %d:\n",i+1); introducir(&tfno[i]); //estructura pasada por referencia if (tfno[i].mes==0) break; } n=i; //guardamos el n mximo de registros intriducidos printf("\nLos datos introducidos son:\n"); for (i=0; i<n; i++) //bucle de presentacin de datos en pantalla { imprimir(tfno[i]); //estructura pasada por valor

} printf("\nPulse una tecla para terminar."); getch(); } void introducir(struct datotfno *tf) //funcin para introduccin de datos por teclado { int scn; do { printf("N de Mes (0=Terminar): "); scn=scanf("%d",&tf->mes); fflush(stdin); } while (tf->mes<0 || tf->mes>12 || scn!=1); if (tf->mes==0) return; do { printf("Ao: "); scn=scanf("%d",&tf->anyo); fflush(stdin); } while (tf->anyo<0 || scn!=1); do { printf("Euros Telefono: "); scn=scanf("%f",&tf->euros); fflush(stdin); } while (tf->euros<0 || scn!=1); } void imprimir(struct datotfno tf) //funcin para presentacin de datos en pantalla { printf("N de Mes: %d\tAo: %d\tConsumo: %.2f\n", tf.mes, tf.anyo, tf.euros); }

PUNTEROS A FUNCIONES: El nombre puro de una funcin (sin los parntesis) representa para el lenguaje C la direccin de memoria de comienzo del cdigo mquina de dicha funcin. Tal direccin puede ser almacenada en una variable de tipo "puntero a funcin", y a travs de esta variable, se puede lanzar la ejecucin de la funcin apuntada y pasarle argumentos de entrada. La declaracin de una variable "puntero a funcin" se realiza as: TipoDevuelto (*NombrePuntero)( ); donde TipoDevuelto es el tipo de dato que devuelve la funcin a la que apuntar este puntero, y donde los parntesis extra alrededor del nombre del puntero y finales son imprescindibles. Si entre los dos parntesis finales no se pone nada, indica que la funcin apuntada puede tener cualquier cantidad y tipo de argumentos de entrada. Ejemplo: int (*pf)( ); la funcin apuntada debe devolver un valor int, y puede tener cualquier cantidad y tipo de argumentos de entrada. Si entre dichos parntesis finales se ponen tipos de datos separados por comas, indicar que la funcin apuntada debe tener forzosamente tales tipos y cantidad de argumentos de entrada, y en ese orden. Esto quita flexibilidad y posibilidades a la variable puntero. Ejemplo: int (*pf)(int, float); la funcin apuntada debe tener dos argumentos de entrada de tipos int y float, y debe devolver un valor int. Para cargar el puntero (una vez declarado) con la direccin de memoria de una funcin, basta con asignarle su nombre puro. Ejemplo: pf = NombreFuncion; Una vez cargado el puntero, para lanzar la ejecucin de la funcin apuntada y pasarle argumentos de entrada caben dos sintaxis posibles: n = pf(argum1, argum2); Equivale a: n = NombreFuncion(argum1, argum2); n = (*pf)(argum1, argum2); Tambin equivale a lo mismo que antes Evidentemente es ms cmoda la primera de las dos sintaxis. Se puede declarar un array de punteros de este tipo, con la siguiente sintaxis: TipoDevuelto (*NombrePuntero[NumElementos]) ( );

EJEMPLO-02. // PUNTEROS A FUNCIONES // Men de operaciones matemticas #include <stdio.h> #include <math.h> #include <conio.h> double cuadrado(double n1); double logaritmo(double n1); double raiz(double n1); int menu(void); //prototipos de las funciones auxiliares

main() { double num=0; //operando double resul; //resultado de la operacin int op; //cdigo de operacin: 0=cuadrado, 1=logaritmo, 2=raiz double (*pfunc[3])(); //array de punteros a funciones pfunc[0] = cuadrado; pfunc[1] = logaritmo; pfunc[2] = raiz; //tambin habra servido asi: double (*pfunc[3])()={cuadrado,logaritmo,raiz}; while (1) { op = menu(); //dibujar men y pedir por teclado la opcin deseada if (op==-1) break; //salir del programa printf("Introduzca numero: "); scanf("%lf", &num); resul = pfunc[op](num); printf("El resultado es: %g\n", resul); printf("Pulse una tecla..."); getch(); } } double cuadrado(double n) { return n*n; } //funcin para obtener el cuadrado

double logaritmo(double n) //funcin para obtener el logaritmo en base 10 { return log10(n); } double raiz(double n) { return sqrt(n); } //funcin para obtener la raiz cuadrada

int menu(void) //pintar menu de opciones en pantalla { int op=0; do { system("cls"); //borrar pantalla, en Unix es: system("clear"); printf("\t1. Cuadrado.\n"); printf("\t2. Logaritmo.\n"); printf("\t3. Raz Cuadrada.\n"); printf("\t0. Salir.\n"); printf("\nSeleccione la operacin a realizar: "); scanf("%d", &op); //pedir por teclado la opcin deseada } while (op<0 || op>3); return (op-1); }

5. RECURSIVIDAD, ARGUMENTOS EN LNEA DE RDENES


FUNCIONES RECURSIVAS: Una funcin "recursiva" es la que, en su ejecucin, se llama a s misma. Cada vez que se llama a s misma, queda pendiente de terminar la ejecucin de la llamada anterior, y se incrementa en una unidad el "nivel de recursin". Toda funcin recursiva necesita tener algn argumento de entrada o variable interna que va variando en cada nueva entrada a la funcin, hasta que se alcance cierta condicin de "fin de recursin", momento a partir del cual comienzan a concluir las llamadas pendientes de la funcin. Cada nueva llamada a la funcin genera en la zona de memoria llamada "pila" ("stack") nuevas instancias de las variables locales y argumentos de entrada de la funcin. Si se producen demasiadas llamadas recursivas, esta "pila" puede llegar a llenarse, causando error de programa. Esto es lo que sucede si la funcin est mal diseada y nunca se llega a la condicin de "fin de recursin". Se puede pedir al compilador (mediante parmetros de compilacin opcionales) que genere un programa con un tamao de pila ms grande de lo normal, para soportar mejor la recursividad. Una funcin recursiva ser buena si el nivel mximo de recursin (nmero mximo de veces sucesivas que la funcin se llama a s misma hasta alcanzar la condicin de finalizacin) no es elevado. Conviene aplicar soluciones recursivas solamente cuando el problema a resolver se puede definir claramente de forma recursiva. En los dems casos (la mayora), conviene utilizar soluciones "iterativas" (mediante bucles), que no demandan tanta capacidad de memoria.
EJEMPLO-01: // CALCULO RECURSIVO DEL FACTORIAL DE UN NUMERO #include <stdio.h> unsigned long factorial(int); main() { int numero; //numero de entrada unsigned long fact; //resultado del factorial do { printf("Dame un numero entero positivo: "); scanf("%d", &numero); } while (numero<0 || numero>25); //el factorial de 26 supera la capacidad de un unsigned long fact = factorial(numero); printf("\nEl factorial de %d es %ld\n", numero, fact); } unsigned long factorial(int n) { if (n==0) return 1; else return n*factorial(n-1); }

EJEMPLO-02: // CUENTA ASCENDENTE Y DESCENDENTE, RECURSIVA E ITERATIVA #include <stdio.h> void ascend(int); void descend(int); main() { int i, numero=0; do { printf("Dame numero positivo:"); scanf("%d", &numero); } while (numero<=0); printf("\nCuenta ascendente recursiva: "); ascend(numero); printf("\nCuenta descendente recursiva: "); descend(numero); printf("\nCuenta ascendente iterativa: "); for (i=0; i<=numero; i++) printf("%d ", i); printf("\nCuenta descendente iterativa: "); for (i=numero; i>=0; i--) printf("%d ", i); } void ascend(int n) { if (n>0) ascend(n-1); printf("%d ", n); } void descend(int n) { printf("%d ", n); if (n>0) descend(n-1); } EJEMPLO-03: // BUSQUEDA BINARIA RECURSIVA #include <stdio.h> int busquedaB(float [], float, int, int); main() { float lista[] = {3, 6.5, 9, 12, 15.3, 18, 21, 24.2, 27, 30, 33, 36, 40, 45, 49, 53, 56, 60}; int numelem = sizeof(lista)/sizeof(float); int posicion; float valor; printf("Introduzca el valor a buscar: "); scanf("%f", &valor); posicion = busquedaB(lista, valor, 0, numelem-1); if (posicion==-1) printf("\nEl valor %g no est en la lista.\n", valor); else printf("\nLa posicion de %g es la %d\n", valor, posicion+1); } int busquedaB(float lista[], float val, int inf, int sup) { int mitad = (inf+sup)/2; if (inf>=sup && lista[inf]!=val)

return -1; if (lista[mitad]==val) return mitad; if (val>lista[mitad]) return busquedaB(lista, val, mitad+1, sup); else return busquedaB(lista, val, inf, mitad-1); }

ARGUMENTOS EN LNEA DE RDENES: Un programa de lenguaje C puede recibir argumentos de entrada desde la lnea de comandos del sistema operativo, al ponerse en marcha el programa. Para conseguirlo, la cabecera de la funcin main() debe ser la siguiente: int main(int argc, char *argv[]); o bien su equivalente: int main(int argc, char **argv); Las variables argc y argv son suministradas desde el sistema operativo. La variable argc indica el n de argumentos pasados desde la lnea de comandos, mas uno. La variable argv representa un array de punteros a cadenas de caracteres suministradas por el sistema operativo: -- argv[0] apunta a una cadena que contiene el nombre completo del programa ejecutable, incluyendo su extensin (.exe) y la ruta de directorios donde se encuentra. -- argv[1] apunta a la cadena correspondiente al primer argumento pasado desde la lnea de comandos (si existe). -- argv[2] apunta a la cadena correspondiente al segundo argumento (si existe), Y sucesivamente con los dems argumentos (no hay lmite a la cantidad de argumentos pasados). Todos los argumentos de la lnea de comandos son pasados como cadenas de caracteres. Si deseamos obtener su valor numrico, tendremos que aplicarles funciones conversoras tales como atoi() o atof(). En la lnea de comandos, los distintos argumentos de entrada deben ir separados por un espacio en blanco. La funcin main() puede retornar un dato int al sistema operativo, con el que informar del xito o no de la ejecucin del programa. Si main() no incluye sentencia return(n) o exit(n) para devolver tal resultado, devolver automticamente un valor 0.
EJEMPLO-04: // ARGUMENTOS EN LA LINEA DE ORDENES void main(int argc, char *argv[]) { int i; if (argc<2) printf("No hay argumentos para el programa %s\n", argv[0]); else { printf("Nombre del programa: %s\n\n", argv[0]); printf("Argumentos pasados: %d\n\n", argc-1); for (i=1; i<argc; i++) printf("Argumento %d: %s\tValor numrico entero: %d\n", i, argv[i], atoi(argv[i])); } } EJEMPLO-05: // ARGUMENTOS EN LINEA DE COMANDOS // Men de operaciones matemticas con argumentos en lnea de comandos // Argumento -oX => Indica que la opcin de men elegida es la X // Argumento -nXXX => Indica que el operando elegido es el XXX

#include <stdio.h> #include <math.h> #include <conio.h> double cuadrado(double n1); double logaritmo(double n1); double raiz(double n1); int menu(void); //prototipos de las funciones auxiliares

main(int argc, char *argv[]) { double num=0; //operando de entrada int op; //cdigo de operacin: 0=cuadrado, 1=logaritmo, 2=raiz double (*pfunc[3])()={cuadrado,logaritmo,raiz}; //array de punteros a funciones int hayop=0; //indica si se ha introducido argumento -o int haynum=0; //indica si se ha introducido argumento -n for (i=1; i<argc; i++) if (argv[i][0]=='-' && tolower(argv[i][1])=='o') //si el argumento i-simo es -o { op = atoi(argv[i]+2); //obtener el valor numrico entero despus del -o if (op>0 && op<4) hayop = 1; } else if (argv[i][0]=='-' && tolower(argv[i][1])=='n') //si el argumento i-simo es -n { num = atof(argv[i]+2); //obtener el valor numrico real despus del -n if (num!=0) haynum = 1; } do { if (!hayop) { op = menu(); //dibujar men y pedir por teclado la opcin deseada if (op==-1) break; //salir del programa } if (!haynum) { printf("Introduzca numero (X=Finalizar): "); //Introducir el operando if (scanf("%lf", &num)!=1) break; //salir del programa } printf("El resultado es: %g\n", pfunc[op](num)); //Presentar el resultado en pantalla printf("Pulse una tecla..."); getch(); } while (!hayop || !haynum); } double cuadrado(double n) //funcin para obtener el cuadrado { return n*n; } double logaritmo(double n) //funcin para obtener el logaritmo en base 10 { return log10(n); } double raiz(double n) //funcin para obtener la raiz cuadrada { return sqrt(n); } int menu(void) //pintar menu de opciones en pantalla { int op=0; do { system("cls"); //borrar pantalla, en Unix es: system("clear"); printf("\t1. Cuadrado.\n"); printf("\t2. Logaritmo.\n"); printf("\t3. Raz Cuadrada.\n"); printf("\t0. Finalizar programa.\n"); printf("\nSeleccione la operacin a realizar: "); scanf("%d", &op); //pedir por teclado la opcin deseada } while (op<0 || op>3); return (op-1); }

6. MANEJO BSICO DE FICHEROS


CONCEPTOS FUNDAMENTALES SOBRE FICHEROS: Un fichero o archivo es una coleccin de informacin que almacenamos en un soporte magntico u ptico, para poderla recuperar y manipular en cualquier momento. Un fichero viene identificado por un nombre que suele tener dos partes: el nombre propio y la extensin, ambas separadas por un punto. Los ficheros se pueden agrupar en muchos tipos distintos: ficheros ejecutables (extensiones .exe, .com y .bat en sistemas Windows), ficheros de texto (.txt, .doc, .lst), ficheros fuente (.c, .h), ficheros de grficos (.jpg, .pdf, .tif, .bin), ficheros de aplicacin (.xls, .ppt), ficheros de base de datos (.dat, .dbf), y muchos tipos ms. Los ficheros de tipo "base de datos" almacenan su informacin en sucesivos "registros" de la misma longitud, compuestos en su interior por "campos" de diversos tipos capaces de almacenar informacin numrica y alfabtica. Para manipular un fichero desde lenguaje C, primero hay que "abrirlo" (funcin fopen). Luego podemos realizar dos tipos de operaciones: leer datos o grabar datos en el fichero. Finalmente hay que "cerrar" el fichero (funcin fclose). Al abrir un fichero, se crea un "apuntador de lectura/escritura" que indica la posicin del prximo byte del fichero que se va a acceder (leer o grabar). Una vez ledo o grabado un byte, el apuntador se mueve automticamente al siguiente byte del fichero. Existen funciones para mover este apuntador a cualquier sitio del fichero donde se desee leer o grabar. Todo fichero tiene una "marca de fin de fichero" despus del ltimo byte de contenido del mismo, marca que no ocupa espacio en el disco (es reconocida por el sistema operativo). Una vez abierto un fichero, puede ser accedido en dos modos principales: con acceso "secuencial" (se acceden los bytes uno tras otro consecutivamente desde el principio del fichero) o con acceso "aleatorio" (se acceden los bytes en cualquier orden no consecutivo). FLUJO ASOCIADO A UN FICHERO: Al abrir un fichero, se crean en la memoria una serie de variables que sirven para facilitar su manipulacin y hacerla ms independiente del tipo de dispositivo fsico utilizado. Este conjunto de variables recibe el nombre de "flujo" de control del fichero, y acta como intermediario entre el programa C y el disco magntico donde reside el fichero, al realizar operaciones de lectura y grabacin de datos En lenguaje C, un flujo consta principalmente de una variable estructura (struct) de tipo FILE de pocos bytes, y una zona ms amplia de memoria intermedia llamada "buffer" (suele ser de 512 bytes, aunque este tamao puede ser variado). Con la ayuda del buffer se acelera notablemente el acceso a posiciones cercanas del disco magntico, y la ejecucin de los programas es ms rpida.
EJEMPLO-01: // CAMPOS DE UNA ESTRUCTURA TIPO FILE // declaraciones incluidas en el fichero stdio.h struct _iobuf { char *_ptr; //Puntero a la posicin del buffer donde se realizar la prxima lectura/escritura int _cnt; //Contador de bytes que quedan por leer o grabar en el buffer char *_base; //Direccin de inicio del buffer char _flag; //Datos de modo de acceso y errores producidos char _file; int _charbuf; int _bufsiz; //Tamao del buffer en n de bytes

char *_tmpfname; }; typedef struct _iobuf FILE; // PUNTEROS A FICHEROS ESTANDAR DEL SISTEMA extern FILE _iob[]; #define stdin (&_iob[0]) #define stdout (&_iob[1]) #define stderr (&_iob[2]) #define stdaux (&_iob[3]) #define stdprn (&_iob[4])

Al comenzar la ejecucin de todo programa, el sistema operativo abre automticamente algunos flujos de control de ficheros estandar: stdin (fichero estandar de entrada, el teclado), stdout (fichero estandar de salida, la pantalla) y stderr (fichero estandar para emitir mensajes de error, la pantalla). Puede haber ms ficheros estandar, dependiendo del sistema operativo: stdaux (puerto estandar serie), stdprn (puerto estandar paralelo).

APERTURA DE UN FICHERO: La funcin principal para abrir un fichero en C es fopen. Su prototipo es: FILE *fopen(char *NombreFichero, char *ModoApertura); fopen necesita dos argumentos de entrada de tipo cadena de caracteres: el nombre del fichero (incluyendo ruta de directorios si es necesario) y el "modo de apertura" del fichero. fopen devuelve la direccin de memoria del flujo de control del fichero recin abierto, direccin que debe ser guardada en una variable de tipo "puntero a FILE". Si hay error de apertura, se devuelve el puntero NULL. Los "modos de apertura" posibles son los siguientes: -- "r" Apertura slo para leer. Si el fichero no existe, se produce error de apertura (se devuelve NULL). El apuntador de L/E se coloca al principio del fichero. -- "w" Apertura slo para grabar. Si el fichero no existe se crea. Si el fichero existe, se destruye para comenzar de nuevo. El apuntador de L/E queda al principio del fichero. -- "a" Apertura para grabar al final del fichero existente. Si el fichero no existe se crea. El apuntador de L/E se coloca al final del fichero. -- "r+" Igual que el modo "r", pero el fichero se puede leer y grabar, una vez abierto. -- "w+" Igual que el modo "w", pero el fichero se puede leer y grabar, una vez abierto. -- "a+" Igual que el modo "a", pero el fichero se puede leer y grabar, una vez abierto. En sistemas Windows (no necesario en Unix), a estos modos se puede aadir la letra "t" o "b" (por ejemplo, w+b, at,). La letra "t" (modo texto) hace que el carcter "nueva linea" (\n) se grabe y se lea en el fichero mediante la pareja de bytes de valor 13+10, comportamiento necesario para ficheros de texto. La letra "b" (modo binario) desactiva esa conversin, hace que el carcter "nueva linea" se grabe y lea en el fichero mediante un nico byte de valor 10, comportamiento necesario para ficheros no-texto. Si no se pone nada, se entiende modo texto. CIERRE DE UN FICHERO: La funcin para cerrar un fichero una vez utilizado es fclose. Su prototipo es: int fclose(FILE *puntero); fclose recibe como argumento el puntero a FILE de control del fichero a cerrar. Devuelve el valor entero 0 si se ejecuta correctamente, y el valor EOF (-1) si ha habido error. fclose termina de grabar en disco los datos pendientes que estaban en el buffer, limpia el buffer y elimina de memoria todas las variables del flujo de control del fichero, liberando recursos para que otros ficheros puedan ser abiertos.

Si no se cierra un fichero con fclose, se cerrar automticamente al terminar la ejecucin del programa.
EJEMPLO-02: // FUNCIONES FOPEN Y FCLOSE #include <stdio.h> main() { FILE *pf; pf = fopen("C:\datos.txt", "w+"); if (pf==NULL) { printf("ERROR de apertura del fichero."); exit(1); } //... operaciones de lectura y grabacion del fichero fclose(pf); }

DETECCIN DE ERRORES DE ACCESO A UN FICHERO: Una vez abierto un fichero, si en las operaciones de lectura y grabacin de datos se produce un error, puede ser detectado mediante la funcin ferror, cuyo prototipo es: int ferror(FILE *puntero); ferror devuelve un valor 0 si no ha ocurrido error, y un valor distinto de cero si se produjo error. Se utiliza como valor cierto/falso: if (ferror(pf)) . Una vez producido un error, ferror permanece a valor cierto indefinidamente hasta que se cierra el fichero o hasta que se ejecuta la instruccin clearerr o rewind. DETECCIN DE FIN DE FICHERO: Una vez abierto un fichero, si en una operacin de lectura de datos se alcanza y se lee la "marca de fin de fichero", se activa la funcin feof, cuyo prototipo es: int feof(FILE *puntero); feof devuelve un valor 0 si no se ha ledo el final del fichero, y un valor distinto de cero si se ha ledo. Se utiliza como valor cierto/falso: if (feof(pf)) . Una vez activada feof, permanece a valor cierto indefinidamente hasta que se cierra el fichero o hasta que se ejecuta la instruccin clearerr o rewind. ANULACIN DE ERRORES DE ACCESO A FICHERO: Mientras estn activos alguno de los indicadores ferror o feof, cualquier intento de operacin de lectura o de grabacin de datos en el fichero ser fallido. Para desactivar los indicadores de error de fichero (ferror) y de fin de fichero (feof) se utiliza la funcin clearerr, cuyo prototipo es: void clearerr(FILE *puntero);
EJEMPLO-03: // FUNCIONES FERROR, FEOF Y CLEARERR #include <stdio.h> main() { FILE *pf; pf = fopen("C:\datos.txt", "r"); if (pf==NULL) { printf("ERROR: fichero no existe."); exit(1); } fprintf(pf, "Hola"); //operacin de grabacin incompatible con el modo "r", producir error if (ferror(pf))

{ printf("ERROR: dato no puede grabarse."); clearerr(pf); } while (!feof(pf) && !ferror(pf)) //bucle de lectura secuencial del fichero { //Operacion de lectura de un dato } if (feof(pf)) printf("Fin de fichero alcanzado."); else if (ferror(pf)) printf("Error de acceso al fichero."); fclose(pf); }

IMPRIMIR EN PANTALLA MENSAJES DE ERROR: Para obtener en pantalla mayor informacin sobre el error que se ha producido, utilizamos la funcin perror, cuyo prototipo es: void perror(char *mensaje); perror recibe como argumento una cadena de caracteres, y la imprime en la pantalla (fichero estandar para mensajes de error, stderr) seguida de "dos puntos" (:) y seguida del mensaje de error dado por el sistema operativo (en ingls), terminando con un avance de lnea (\n).
EJEMPLO-04: // FUNCION PERROR #include <stdio.h> main() { FILE *pf; pf = fopen("C:\datos.txt", "r"); if (pf==NULL) { perror("ERROR en datos.txt"); exit(1); } fprintf(pf, "Hola"); //operacin de grabacin incompatible con el modo "r", producir error if (ferror(pf)) { perror("ERROR al grabar"); clearerr(pf); } while (!feof(pf) && !ferror(pf)) //bucle de lectura secuencial del fichero { //Operacion de lectura de un dato } if (feof(pf)) perror("Fin de fichero"); else if (ferror(pf)) perror("ERROR"); fclose(pf); }

Los mensajes de error del sistema estn almacenados en el array de punteros a char sys_errlist. El nmero de elementos de este array viene dado por la variable sys_nerr. El ltimo error producido viene identificado por la variable errno, que se utiliza como ndice en el mencionado array. Para acceder a estas 3 variables del sistema hay que incluir el fichero #include <stdlib.h>

EJEMPLO-05: // IMPRESION DE TODOS LOS MENSAJES DE ERROR DEL SISTEMA #include <stdio.h> #include <stdlib.h> main() { int i; for (i=0; i<sys_nerr; i++) if (*sys_errlist[i]!='\0') //si el mensaje de error no est vaco printf("Error Num %d: %s\n", i, sys_errlist[i]); //imprimir n de error y mensaje printf("\nPulse ENTER para terminar."); getchar(); //perror("Mensaje"); equivale a: printf("Mensaje: %s\n", sys_errlist[errno]); } /* RESULTADO EN PANTALLA: Error Num 0: Error 0 Error Num 2: No such file or directory Error Num 7: Arg list too long Error Num 8: Exec format error Error Num 9: Bad file number Error Num 12: Not enough core Error Num 13: Permission denied Error Num 17: File exists Error Num 18: Cross-device link Error Num 22: Invalid argument Error Num 24: Too many open files Error Num 28: No space left on device Error Num 33: Math argument Error Num 34: Result too large Error Num 36: Resource deadlock would occur Pulse ENTER para terminar. */

POSICIN DEL APUNTADOR DE LECTURA/ESCRITURA: Todo fichero abierto tiene un "apuntador de L/E" que indica el byte en el cual se realizar la prxima operacin de lectura o grabacin en el fichero. Cuando tal operacin es realizada, el apuntador es empujado automticamente hacia delante para apuntar al siguiente byte que ser ledo o grabado. Para conocer en qu posicin se encuentra actualmente el apuntador de L/E utilizamos la funcin ftell, cuyo prototipo es: long ftell(FILE *puntero); Esta funcin devuelve el nmero de byte donde est el apuntador, contando desde el principio del fichero, o bien el valor -1 si hay un error. La posicin del primer byte del fichero es la n 0. Para mover el apuntador de L/E a la posicin deseada del fichero, donde se realizar la siguiente operacin de lectura/grabacin, utilizamos la funcin fseek, cuyo prototipo es: int fseek(FILE *puntero, long desplaz, int posinicio); La funcin fseek mueve el apuntador a una posicin desplazada "desplaz" bytes desde la posicin de inicio "posinicio". Esta posicin de inicio puede tener 3 valores posibles, dados por 3 constantes simblicas (en maysculas): -- SEEK_SET (valor 0): Desde el principio del fichero. -- SEEK_CUR (valor 1): Desde la posicin actual del apuntador de L/E. -- SEEK_END (valor 2): Desde el final del fichero. fseek devuelve 0 si se ejecuta correctamente, y distinto de 0 si hay error.

Para mover el apuntador de L/E al comienzo del fichero tenemos la funcin rewind, cuyo prototipo es: void rewind(FILE *puntero); rewind(pf) hace lo mismo que fseek(pf, 0, SEEK_SET) pero adems desactiva los indicadores de error ferror y feof, cosa que no hace la funcin fseek.
EJEMPLO-06: // FUNCIONES PARA POSICIONAMIENTO DEL APUNTADOR DE L/E #include <stdio.h> main() { FILE *pf; pf = fopen("Prog06-Ejemplo06.c", "r"); fseek(pf, 0, SEEK_END); //mueve el apuntador al final del fichero, sobre la marca de "fin de fichero" //estando al final del fichero, ftell nos dira el tamao del mismo printf("El tamao del fichero es: %ld", ftell(pf)); fseek(pf, -10, SEEK_CUR); //retrocede el apuntador 10 bytes desde la posicin actual fseek(pf, 10, SEEK_SET); //mueve el apuntador al byte n 10 del fichero (el primer byte es el 0) fseek(pf, 15, SEEK_CUR); //mueve el apuntador al byte n 25 del fichero (avanza 15 bytes) fseek(pf, 0, SEEK_SET); //mueve el apuntador al comienzo del fichero rewind(pf); //igual que el anterior, pero poniendo a cero ferror y feof fseek(pf, -10, SEEK_SET); //no es posible, mueve el apuntador al comienzo del fichero, no causa error fseek(pf, 10, SEEK_END); //no es posible, mueve el apuntador al final del fichero, no causa error fclose(pf); }

CONTROL DEL BUFFER DEL FICHERO: La funcin fflush graba en el fichero especificado el contenido del buffer que estaba pendiente de ser grabado, y vaca el buffer. Si el fichero esta abierto nicamente para leer, el buffer simplemente se vaca. Su prototipo es: int fflush(FILE *pf); La funcin devuelve 0 si se ejecut correctamente, y un EOF (-1) en caso contrario.

7. LECTURA/GRABACIN DE DATOS EN FICHEROS


LECTURA Y GRABACIN DE DATOS CARCTER A CARCTER: Una vez abierto un fichero, sus datos pueden ser grabados y ledos byte a byte, con las funciones fputc y fgetc. La funcin fputc tiene como prototipo: int fputc(int car, FILE *puntero); Esta funcin graba el primer byte del argumento car en la posicin actual del apuntador de L/E, avanza el apuntador al siguiente byte y devuelve como resultado el carcter escrito, o bien un valor EOF (-1) si ha ocurrido un error. La funcin fgetc tiene como prototipo: int fgetc(FILE *puntero); Esta funcin lee el byte que se encuentra en la posicin actual del apuntador de L/E, avanza el apuntador al siguiente byte y devuelve como resultado el carcter ledo, o bien un valor EOF (1) si ha ocurrido un error. En ambas funciones no conviene usar el dato devuelto EOF como detector de error en la operacin, ya que el dato grabado o ledo correctamente puede ser de valor -1 (EOF). Hay que utilizar la funcin ferror o feof para detectar los posibles errores.
EJEMPLO-01: // FUNCIONES FPUTC Y FGETC #include <stdio.h> main() { FILE *pf; char cad[200], cad2[200]; int i=0; printf("Introduce un texto a grabar:\n"); gets(cad); if ((pf=fopen("texto", "w+"))==NULL) { perror("ERROR al abrir el fichero"); exit(1); } while (cad[i]!=0 && ferror(pf)==0) //sirve tambin: while (cad[i] && !ferror(pf)) { fputc(cad[i], pf); i++; } if (ferror(pf)) perror("ERROR al grabar en el fichero"); rewind(pf); i=0; //apuntador de L/E al principio del fichero y desactivacin de ferror while (!feof(pf) && !ferror(pf)) { cad2[i] = fgetc(pf); i++; } cad2[i-1]=0; //caracter nulo de final de cadena if (ferror(pf)) perror("ERROR al leer el fichero"); printf("El texto grabado es: %s", cad2); fclose(pf); }

GRABACIN DE CADENAS DE CARACTERES: Una vez abierto un fichero, sus datos pueden ser grabados y ledos en bloques de cadenas de caracteres, con las funciones fputs y fgets. La funcin fputs tiene como prototipo: int fputs(char *cad, FILE *puntero); Esta funcin graba los caracteres que hay en la cadena cad en la posicin actual del apuntador de L/E (sin incluir el carcter nulo de final de cadena), avanza el apuntador de L/E en el fichero

y devuelve como resultado un nmero no negativo, o bien un valor EOF (-1) si ha ocurrido un error. Para recuperar posteriormente las cadenas as grabadas, conviene grabar el carcter "nueva lnea" (\n) despus de cada cadena, para que acte como separador: while (gets(cadena)!=NULL) { fputs(cadena, pf); fputc('\n', pf); }

LECTURA DE CADENAS DE CARACTERES: La funcin fgets tiene como prototipo: char *fgets(char *cad, int nmax, FILE *puntero); Esta funcin lee desde el fichero la cadena de caracteres que se encuentra en la posicin actual del apuntador de L/E, la almacena en la variable de memoria cad (aportando el carcter nulo final \0), avanza el apuntador de L/E en el fichero y devuelve como resultado un puntero a la variable de memoria recin leda, o bien un valor NULL (puntero 0) si ha ocurrido un error o se ha ledo la marca de fin de fichero. El argumento nmax coincidir habitualmente con el tamao declarado para el array de char de la cadena cad. La cadena leda en el fichero termina ante 3 posibles situaciones: cuando se lee el carcter "nueva lnea" (\n, este carcter pasa a la variable de memoria), o bien cuando se lee una cantidad de bytes dada por nmax-1 (hay que dejar un byte para el carcter \0), o bien cuando se lee la marca de fin de fichero. La funcin de ficheros fgets reemplaza ventajosamente a la funcin gets de lectura de cadenas desde teclado, ya que permite limitar la cantidad mxima de caracteres asignados a la variable de memoria y evitar su desbordamiento: char cad[10]; gets(cad); Permite leer ms de 10 caracteres desde el teclado sobre la variable cad fgets(cad, 10, stdin); Slo permite leer 9 caracteres desde el fichero teclado (stdin)
EJEMPLO-02: // FUNCIONES FPUTS Y FGETS #include <stdio.h> #include <stdlib.h> #include <string.h> #define N 200 main() { FILE *pf; char cad[N], nomfi[13]; printf("Introduce nombre del fichero a grabar: "); fgets(nomfi, 13, stdin); //leer hasta 12 caracteres fflush(stdin); //borrar posibles caracteres de ms escritos if (nomfi[strlen(nomfi)-1]=='\n') //eliminar posible caracter final \n nomfi[strlen(nomfi)-1] = 0; if ((pf=fopen(nomfi, "w+"))==NULL) //abrir fichero { perror("ERROR al abrir el fichero %s", nomfi); exit(1); } printf("Introduzca textos terminados con ENTER; pulse CTRL+Z para terminar\n\n"); while (fgets(cad, N, stdin)!=NULL) //leer desde el teclado, mximo N-1 caracteres { fputs(cad, pf); fflush(stdin); //grabar en el fichero y borrar buffer del teclado if (cad[strlen(cad)-1]!='\n') fputc('\n',pf); //grabar \n si falta if (ferror(pf)) { perror("Error al grabar en el fichero"); break; } } rewind(pf); //apuntador de L/E al principio del fichero y desactivacin de ferror

printf("\nLos textos ledos son:\n\n"); while (fgets(cad, N, pf)!=NULL) //leer desde el fichero hasta el fin de fichero printf("%s", cad); if (ferror(pf)) perror("ERROR al leer en el fichero"); fclose(pf); }

LECTURA Y GRABACIN DE DATOS CON FORMATO: Una vez abierto un fichero, sus datos pueden ser grabados y ledos con los formatos utilizados por las funciones printf y scanf, mediante las funciones fprintf y fscanf. La funcin fprintf tiene la siguiente declaracin prototipo: int fprintf(FILE *pf, char *formato, ); Acta de forma idntica a la funcin printf, imprimiendo datos de varios tipos y con diversos formatos, pero en lugar de dirigirlos hacia la pantalla los graba en el fichero especificado por el puntero pf, en el lugar donde se encuentre su apuntador de L/E. Despus de la cadena "formato" pueden venir ms variables y expresiones, cuyos valores se grabarn en el fichero. La funcin fprintf devuelve un entero que es la cantidad de bytes grabados en el fichero. Si el puntero a fichero es stdout (la pantalla), fprintf es equivalente a printf: printf("n = %d", n); equivale a fprintf(stdout, "n = %d", n); La funcin fscanf tiene la siguiente declaracin prototipo: int fscanf(FILE *pf, char *formato, ); Acta de forma idntica a la funcin scanf, leyendo desde el fichero (ya no desde el teclado) datos de varios tipos y con diversos formatos, y guardndolos en las variables de memoria especificadas, que son pasadas por referencia (con &) despus de la cadena "formato". La funcin fscanf devuelve un entero que es la cantidad de datos correctamente ledos y asignados a variables de memoria. Si el puntero a fichero es stdin (el tyeclado), fscanf es equivalente a scanf: scanf("%d %d", &n, &m); equivale a fscanf(stdin, "%d %d", &n, &m);
EJEMPLO-03: // FUNCIONES FPRINTF Y FSCANF #include <stdio.h> main() { FILE *pf; char cad[200]; int i=1; long a=99999; float b=3.14; if ((pf=fopen("datos", "r"))==NULL) { //El fichero no existe, lo abrimos en modo escritura if ((pf=fopen("datos", "w"))==NULL) { perror("ERROR al abrir el fichero"); exit(1); } fprintf(pf, "Linea %d: %7ld %9.2f\n", i, a, b); fprintf(pf, "Linea %d: %7ld %9.2f\n", ++i, a/2, b*2); printf("El fichero no existe, lo creamos. Ejecute de nuevo el programa."); } else { //El fichero ya existe, queda abierto en modo lectura printf("Contenido de texto del fichero:\n"); fgets(cad, 200, pf); printf("%s", cad);

fgets(cad, 200, pf); printf("%s", cad); rewind(pf); printf("\nVariables leidas con fscanf:\n"); fscanf(pf, "%s %d: %ld %f", cad, &i, &a, &b); printf("cad=%s i=%d a=%ld b=%.2f\n", cad, i, a, b); fscanf(pf, "%s %d: %ld %f", cad, &i, &a, &b); printf("cad=%s i=%d a=%ld b=%.2f\n", cad, i, a, b); } fclose(pf); } /* RESULTADO EN PANTALLA: El fichero no existe, lo creamos. Ejecute de nuevo el programa. Contenido de texto del fichero: Linea 1: 99999 3.14 Linea 2: 49999 6.28 Variables leidas con fscanf: cad=Linea i=1 a=99999 b=3.14 cad=Linea i=2 a=49999 b=6.28 */

GRABACIN DE REGISTROS: Una vez abierto un fichero, sus datos pueden ser grabados y ledos en bloques de tipo "registro" (variables de tipo struct formadas por varios campos) mediante las funciones fwrite y fread. La funcin fwrite tiene la siguiente declaracin prototipo: typedef unsigned int size_t; size_t fwrite(void *dirinicio, size_t tamelem, size_t numelems; FILE *pf); La funcin fwrite est pensada para transferir de memoria a fichero un array de elementos (tantos como "numelems") y cada elemento de "tamelem" bytes de tamao. Realiza una copia exacta de la zona de memoria que comienza en la direccin "dirinicio" y que ocupa un n de bytes dado por el producto "tamelem x numelems", sobre el fichero "pf" en el lugar donde se encuentra actualmente su apuntador de L/E. Esta funcin fwrite puede sustituir a algunas de las funciones de grabacin vistas hasta ahora: char car, cadena[40]; fputc(car, pf); equivale a: fwrite(&car, 1, 1, pf); fputs(cadena, pf); equivale a: fwrite(cadena, strlen(cadena), 1, pf); LECTURA DE REGISTROS: La funcin fread es muy similar a fwrite, y tiene la siguiente declaracin prototipo: size_t fread(void *dirinicio, size_t tamelem, size_t numelems; FILE *pf); La funcin fread est pensada para transferir de fichero a memoria un array de elementos (tantos como "numelems") y cada elemento de "tamelem" bytes de tamao. Realiza una copia exacta de los bytes del fichero "pf" (donde se encuentra actualmente su apuntador de L/E) sobre la zona de memoria que comienza en la direccin "dirinicio" y que ocupa un n de bytes dado por el producto "tamelem x numelems". Esta funcin fread puede sustituir a algunas de las funciones de lectura vistas hasta ahora: char car, cadena[40]; car = fgetc(pf); equivale a: fread(&car, 1, 1, pf); fgets(cadena, sizeof(cadena), pf); equivale a: fread(cadena, sizeof(cadena)-1, 1, pf);

EJEMPLO-04: // FUNCIONES FWRITE Y FREAD // Programa para cargar desde teclado datos de consumo telefnico mensual #include <stdio.h> #define MAX 50 //n de elementos del array de estructuras struct datotfno //declaracin del tipo estructura { int mes,anyo; float euros; }; void introducir(struct datotfno *tf); //declaraciones prototipo de funciones auxiliares void imprimir(struct datotfno tf); main() { int n=0,i; FILE *pf; struct datotfno tfno[MAX]; //array de estructuras for (i=0; i<MAX; i++) tfno[i].mes = tfno[i].anyo = tfno[i].euros = 0; //poner a 0 todos los campos del array if ((pf=fopen("datostf.dat","r+"))==NULL) //El fichero no existe, lo creamos if ((pf=fopen("datostf.dat", "w+"))==NULL) { printf("ERROR de apertura"); exit(1); } else { //El fichero existe, leemos sus registros fread(tfno, sizeof(struct datotfno), MAX, pf); //leer todos los registros en el fichero for (n=0; n<MAX && tfno[n].mes!=0; n++); //obtener numero n de registros introducidos } system("cls"); //borrar pantalla, en Unix es: system("clear"); printf("Introduzca los datos de consumo telefnico:\n\n") for (i=n; i<MAX; i++) //bucle para introduccin de registros { printf("\nRegistro N %d:\n",i+1); introducir(&tfno[i]); //estructura pasada por referencia if (tfno[i].mes==0) break; } n=i; //guardamos el n mximo de registros introducidos printf("\nLos datos introducidos son:\n"); for (i=0; i<n; i++) //bucle de presentacin de datos en pantalla imprimir(tfno[i]); //estructura pasada por valor printf("\nPulse una tecla para terminar."); getch(); rewind(pf); fwrite(tfno, sizeof(struct datotfno), MAX, pf); //grabar todos los registros en el fichero fclose(pf); } void introducir(struct datotfno *tf) //funcin para introduccin de datos por teclado { int scn; do { printf("N de Mes (0=Terminar): "); scn=scanf("%d",&tf->mes); fflush(stdin); } while (tf->mes<0 || tf->mes>12 || scn!=1); if (tf->mes==0) return; do { printf("Ao: "); scn=scanf("%d",&tf->anyo); fflush(stdin); } while (tf->anyo<0 || scn!=1); do { printf("Euros Telefono: "); scn=scanf("%f",&tf->euros); fflush(stdin); } while (tf->euros<0 || scn!=1); } void imprimir(struct datotfno tf) //funcin para presentacin de datos en pantalla { printf("N de Mes: %d\tAo: %d\tConsumo: %.2f\n", tf.mes, tf.anyo, tf.euros); }

8. MS SOBRE FICHEROS
FICHEROS TEMPORALES: La funcin tmpfile crea un fichero temporal que es automticamente eliminado cuando el fichero es cerrado o cuando el programa termina normalmente. El fichero temporal es abierto en modo "w+b" (lectura/grabacin en modo binario), y el nombre dado al fichero en el disco es aleatorio. Este tipo de ficheros es til cuando deseamos grabar en disco cierta informacin transitoria que no queremos que permanezca ms all de la finalizacin del programa. Su prototipo es: FILE *tmpfile(void); No necesita argumento de entrada. Devuelve NULL si no se pudo crear el fichero, distinto de NULL si la creacin tuvo xito. Ejemplo: FILE *pf; if ((pf = tmpfile())==NULL) printf("No se puede abrir el fichero temporal"); ELIMINACION DE FICHEROS: Para eliminar un nico fichero existe la funcin remove, cuyo prototipo es: int remove(char *nombre); Recibe como argumento una cadena de caracteres con el nombre del fichero a eliminar (puede incluir la ruta de directorios donde se encuentra). Devuelve 0 si la operacin tuvo xito, y distinto de 0 en caso contrario (quiz porque el fichero no exista). El fichero a eliminar no debe estar abierto con fopen. Para eliminar varios ficheros utilizando los caracteres comodn del sistema operativo, se utiliza la funcin system, que invocar al comando de eliminacin de ficheros "delete" en sistemas Windows o "rm" en sistemas Unix. Ejemplo: system ("delete c:\*.exe"); CAMBIAR EL NOMBRE DE FICHEROS: Para renombrar un nico fichero existe la funcin rename, cuyo prototipo es: int rename(char *antiguo, char *nuevo); Recibe como argumento dos cadenas de caracteres con el nombre del fichero antiguo (puede incluir la ruta de directorios donde se encuentra) y el nuevo nombre deseado. Devuelve 0 si la operacin tuvo xito, y distinto de 0 en caso contrario. El fichero a renombrar no debe estar abierto con fopen. Para renombrar varios ficheros utilizando los caracteres comodn del sistema operativo, se utiliza la funcin system, que invocar al comando de cambio de nombre de ficheros "rename" en sistemas Windows o comando "mv" en Unix. Ejemplo: system ("rename c:\*.txt *.dat"); COPIAR FICHEROS: Para copiar un fichero no existe funcin estandar de C. Para copiar uno o varios ficheros utilizando los caracteres comodn del sistema operativo, se utiliza la funcin system, que invocar al comando de copia de ficheros "copy" en sistemas Windows o comando "cp" en Unix. Ejemplo: system ("copy c:\fich.txt c:\fich2.txt >nul");
// BORRAR, RENOMBRAR Y COPIAR FICHEROS #include <stdio.h> int existefich(char *cad); int copiafich(char *orig, char *dest); int renamefich(char *orig, char *nuevo); int borrafich(char *nomfic); main() { char nom[20];

printf("Nombre del fichero a procesar: "); gets(nom); //debe ser un fichero existente if (!nom[0]) exit(1); //si pulsamos Enter sin escribir nada if (!existefich(nom)) { printf("Fichero %s no encontrado", nom); getch(); exit(1); } if (copiafich(nom, "fich2.txt")) { printf("Fichero %s copiado\n", nom); getch(); } else { printf("No se pudo copiar el fichero %s", nom); getch(); exit(1); } if (rename(nom, "fich0.txt")==0) { printf("Fichero %s renombrado\n", nom); getch(); } //equivale a: if (renamefich(nom, "fich0.txt")) { ...... } else { printf("No se pudo renombrar el fichero %s", nom); getch(); exit(1); } if (remove("fich0.txt")==0) { printf("Fichero fich0 eliminado\n"); getch(); } //equivale a: if (borrafich("fich0.txt")) { ...... } else { printf("No se pudo eliminar el fichero fich0"); getch(); exit(1); } if (rename("fich2.txt", nom)==0) { printf("Fichero fich2 renombrado\n"); getch(); } else { printf("No se pudo renombrar el fichero fich2"); getch(); exit(1); } } int existefich(char *cad) //devuelve 1 si el fichero "cad" indicado existe, 0 en caso contrario { FILE *pf; if ((pf=fopen(cad, "r"))==NULL) return 0; else { fclose(pf); return 1; } } int copiafich(char *orig, char *dest) //copia el fichero "orig" en "dest", devuelve 1 si hay exito { char *p; if (!existefich(orig)) return 0; //si no existe el fichero origen, abandonamos p = (char *)malloc(12+strlen(orig)+strlen(dest)); //reserva dinmica de memoria if (p==NULL) { printf("No hay memoria"); return 0; } strcpy(p, "copy "); strcat(p, orig); strcat(p, " "); //composicin del comando a ejecutar strcat(p, dest); strcat(p, " >nul"); system(p); //El comando queda as: copy fichero.dat fich2.txt >nul //Para Unix debera ser: cp fichero.dat fich2.txt free(p); //liberar la memoria reservada return existefich(dest); //comprobamos si existe el fichero copia } int renamefich(char *orig, char *nuevo) //renombra fichero "orig" con el nombre "nuevo", //devuelve 1 si bien (para Windows) { char *p; if (!existefich(orig)) return 0; //si no existe el fichero origen, abandonamos p = (char *)malloc(9+strlen(orig)+strlen(nuevo)); //reserva dinmica de memoria if (p==NULL) { printf("No hay memoria"); return 0; } strcpy(p, "rename "); strcat(p, orig); strcat(p, " "); strcat(p, nuevo); //composicin del comando system(p); //El comando queda as: rename fich1.txt fich2.txt //Para Unix debera ser: mv fich1.txt fich2.txt free(p); //liberar la memoria reservada return (!existefich(orig) && existefich(nuevo)); //comprobamos la existencia de los ficheros } int borrafich(char *nomfic) //borra el fichero "nomfic", devuelve 1 si hay xito { char *p; if (!existefich(nomfic)) return 0; //si no existe el fichero origen, abandonamos p = (char *)malloc(8+strlen(nomfic)); //reserva dinmica de memoria if (p==NULL) { printf("No hay memoria"); return 0; } strcpy(p, "delete "); strcat(p, nomfic); //composicin del comando system(p); //El comando queda as: delete fich.txt //Para Unix debera ser: rm fich.txt free(p); //liberar la memoria reservada return !existefich(nomfic); //comprobamos si an existe el fichero borrado }

ESCRIBIR DATOS EN LA IMPRESORA: Para enviar datos hacia la impresora hay que usar como flujo de salida el fichero estandar "stdprn" correspondiente al "puerto paralelo" de la impresora (si el ordenador dispone de l), o bien crear un flujo de salida ("w") dirigido hacia el dispositivo al que est conectado la impresora, usando el nombre de dispositivo utilizado por el sistema operativo: -- En Windows: "lpt1", "lpt2", "prn", "com1", "com2", "com3", etc. -- En Unix: "/dev/lp0", "/dev/lp1", etc. Ejemplo: FILE *pfs; if ((pfs = fopen("lpt1", "w"))==NULL) printf("Impresora no disponible"); else { fprintf(pfs, "Este texto se escribe en la impresora \f"); fclose(pfs); }
EJEMPLO-02: // USO DE LA IMPRESORA // Impresin de un fichero de texto #include <stdio.h> main() { FILE *pfe, *pfs; char cad[20], car, com[30]; if ((pfs=fopen("lpt1", "w"))==NULL) //abrimos el dispositivo "lpt1" (impresora puerto paralelo) { perror("Impresora no disponible"); exit(1); } printf("Nombre del fichero a imprimir: "); gets(cad); if (cad[0]==0) exit(1); //Si pulsamos Enter sin escribir nada if ((pfe=fopen(cad, "r"))==NULL) //abrimos el fichero a imprimir { perror("Fichero no encontrado"); fclose(pfs); exit(1); } while (car=fgetc(pfe), !feof(pfe) && !ferror(pfe)) //leemos el fichero byte a byte fputc(car, pfs); //imprimimos en la impresora byte a byte if (ferror(pfe)) perror("ERROR al leer el fichero"); else printf("El fichero ha sido enviado a la impresora\n"); fclose(pfe); fclose(pfs); getch(); //Otra forma de imprimir un fichero utilizando la funcin system strcpy(com, "copy "); strcat(com, cad); strcat(com," lpt1 >nul"); //componemos el comando system(com); //El comando queda as: copy fichero.txt lpt1 >nul printf("\nEl fichero se ha imprimido de nuevo"); getch(); }

VER DIRECTORIOS DE FICHEROS: No existe una funcin estandar de C para obtener informacin de los directorios y ficheros existentes en los discos, dado que los diversos sistemas operativos los manipulan de diferentes maneras. Una forma de conseguirlo es utilizar la funcin system para invocar al comando de visualizacin de directorios del sistema operativo utilizado (comando "dir" en Windows o "ls" en Unix) y redirigir la salida de pantalla (>) hacia un fichero de texto que luego abriremos para leerlo y procesarlo. Ejemplo: system("dir *.c > dir.txt");

EJEMPLO-03: // VER DIRECTORIOS EN PANTALLA #include <stdio.h> main() { char nom[30], *p, lin[100]; FILE *pf; printf("Indique directorio a visualizar: "); gets(nom); if (nom[0]==0) exit(1); //si pulsamos Enter sin escribir nada //supongamos que hemos tecleado: C:\DATOS\*.* p = (char *)malloc(15+strlen(nom)); //reservamos memoria dinmicamente if (p==NULL) { printf("No hay memoria"); exit(1); } strcpy(p, "dir "); strcat(p, nom); strcat(p, " > dir.txt"); //componemos el comando a ejecutar system(p); //El comando ha quedado as: dir C:\DATOS\*.* > dir.txt free(p); //liberamos la zona reservada dinmicamente if ((pf=fopen("dir.txt", "r"))==NULL) //abrimos el fichero de texto dir.txt { printf("El fichero dir.txt no ha sido creado"); exit(1); } printf("El contenido del directorio es:\n\n"); while (fgets(lin, 100, pf)!=NULL) //leemos el fichero linea a linea { printf("%s", lin); lin[0]=0; } //imprimimos en pantalla linea a linea if (lin[0]!=0) printf("%s\n", lin); //si la ltima lnea leda tena contenido fclose(pf); getch(); //cerrar fichero y esperar tecla remove("dir.txt"); //borrar el fichero de texto }

ACCESO SECUENCIAL A FICHEROS: Es el tipo ms simple de acceso a un fichero: los datos que se escriben en el fichero son colocados automticamente una tras otro, y cuando se leen, se comienza desde el primero y se leen consecutivamente hasta alcanzar el final del fichero. Es el tipo de acceso habitual para ficheros de texto, en los que se escribe toda la informacin desde el principio hasta el final con algn tipo de bucle y se lee de la misma forma. El bucle de lectura siempre utilizar como condicin de finalizacin la funcin feof: while (!feof(pf) )
EJEMPLO-04: // ACCESO SECUENCIAL A REGISTROS // Programa para cargar desde teclado datos de consumo telefnico mensual #include <stdio.h> #include <conio.h> #include <stdlib.h> int introducir(struct datotfno *tf); void imprimir(FILE *pf); char sino(char *mensaje); struct datotfno { int mes,anyo; float euros; }; //declaracin del tipo estructura

main() { int n, numregs=0, tamreg=sizeof(struct datotfno); struct datotfno dato; FILE *pf; system("cls"); //borrar pantalla if ((pf=fopen("datostf.dat","r+"))==NULL) //El fichero no existe, lo creamos if ((pf=fopen("datostf.dat", "w+"))==NULL) { printf("ERROR de apertura"); exit(1); } else { //El fichero existe fseek(pf,0,SEEK_END); //Apuntador L/E al final del fichero numregs = ftell(pf)/tamreg; //obtenemos el n de registros del fichero printf("Hay %d registros en el fichero. ",numregs); if (sino("Desea listarlos?")=='S') imprimir(pf); //si se responde 'S' a la pregunta } if (sino("Desea introducir datos?")=='S') { printf("Introduzca los datos de consumo telefnico:\n"); do //bucle para introduccin de registros de forma secuencial { printf("\nRegistro N %d:\n",numregs+1); if (n=introducir(&dato)) //instruccin de asignacin, no de comparacin { fwrite(&dato, tamreg, 1, pf); numregs++; } //grabar registro e incrementar numregs } while (n); //continua mientras n!=0 } if (sino("Desea listar todos los registros?")=='S') imprimir(pf); printf("\nPulse una tecla para terminar."); getch(); fclose(pf); } int introducir(struct datotfno *tf) //funcin para introduccin de datos por teclado { int scn; do { printf("N de Mes (0=Terminar): "); scn=scanf("%d",&tf->mes); fflush(stdin); } while (tf->mes<0 || tf->mes>12 || scn!=1); if (tf->mes==0) return 0; //retorna 0 para terminar do { printf("Ao: "); scn=scanf("%d",&tf->anyo); fflush(stdin); } while (tf->anyo<0 || scn!=1); do { printf("Euros Telefono: "); scn=scanf("%f",&tf->euros); fflush(stdin); } while (tf->euros<0 || scn!=1); return 1; //retorna 1 para continuar } void imprimir(FILE *pf) //funcin para presentacin de datos en pantalla con acceso secuencial { struct datotfno d; int n=0; system("cls"); rewind(pf); //Apuntador de L/E al principio del fichero fread(&d, sizeof(d), 1, pf); //leer registro while (!feof(pf) && !ferror(pf)) //preguntar por indicadores de error { printf("\nN de Mes: %d\tAo: %d\tConsumo: %.2f", d.mes, d.anyo, d.euros);

if (++n>=23) //Controlamos n de linea en pantalla para evitar "scrolling" { printf("\nPulse una tecla para continuar..."); getch(); n=0; system("cls"); } fread(&d, sizeof(d), 1, pf); //leer registro } clearerr(pf); printf("\n\n"); } char sino(char *mensaje) //funcin para leer respuesta por teclado de tipo S/N { char resp; printf("%s (S/N): ", mensaje); //imprimir el mensaje do resp = toupper(getch()); //leer tecla y convertirla a maysculas while (resp!='S' && resp!='N'); printf("%c", resp); //ver respuesta en pantalla return resp; //retornamos el caracter ledo } //desactivar indicadores de error

9. ACCESO ALEATORIO A FICHEROS


ACCESO ALEATORIO A FICHEROS: Con acceso aleatorio los datos se escriben y se leen en el fichero en cualquier orden, posicionando el apuntador de Lectura/Escritura en el lugar deseado del fichero y a continuacin realizando la operacin de grabacin o lectura. Una vez realizada, el apuntador avanza automticamente hasta el byte siguiente al ltimo grabado o ledo. Es el tipo de acceso habitual para ficheros de bases de datos, en los que la informacin est almacenada en forma de registros consecutivos de igual longitud, que contienen en su interior campos de diversos tipos (el mismo concepto que las variables de tipo struct). Se maneja aqu el concepto de nmero de registro identificativo de cada registro, comenzando por el primer registro del fichero de n 0. Se utiliza intensivamente la funcin fseek para posicionar el apuntador de L/E, y la funcin ftell para obtener el n de byte donde se encuentra dicho apuntador. EJEMPLO PRCTICO: Disear una aplicacin de base de datos de Diccionario Espaol-Ingls que maneje dos ficheros paralelos, uno para almacenar las palabras espaolas y el otro para las palabras inglesas. Cada registro en ambos ficheros consta de un nico campo de tipo texto de 21 bytes para almacenar una palabra (20 caracteres + nulo). Las palabras equivalentes en ingls y espaol estarn almacenadas en el mismo n de registro de su fichero. La aplicacin tendr un men principal desde el cual se realizarn las operaciones habituales de mantenimiento de ficheros: altas de nuevos registros, modificacin o eliminacin de registros, bsqueda de palabras en espaol o en ingls, ordenacin alfabtica de los registros en espaol o ingls y listado del diccionario completo en pantalla.
// ACCESO ALEATORIO A FICHEROS // Diccionario Espaol-Ingles / Ingles-Espaol con acceso directo #define ANCHO 21 #define setpos(a,b) _settextposition(a,b) //funcin para posicionar el cursor de texto en la fila a, columna b #include <stdio.h> #include <graph.h> int menudicc(void); //prototipos de funciones void anadir(void); void modificar(void); void eliminar(void); void listar(void); void ordenar(FILE *p1, FILE *p2); int buscar(FILE *p1, FILE *p2, int espanol); void leecad(int,int,char *); void rellenablancos(char *cad); char *msg="Pulse una tecla..."; //puntero global a mensaje de texto habitual FILE *pf1, *pf2; //punteros a FILE globales main() { int menu,numdatos,i; if ((pf1=fopen("spanish.dat","r+"))==NULL) { pf1=fopen("spanish.dat","w+"); } //abrir el fichero para no perder los datos ya existentes /*Apertura alternativa: if ((pf1=fopen("spanish.dat","a+"))!=NULL) rewind(pf1); */

if ((pf2=fopen("english.dat","r+"))==NULL) { pf2=fopen("english.dat","w+"); } /*Apertura alternativa: if ((pf2=fopen("english.dat","a+"))!=NULL) rewind(pf2); */ if (pf1==NULL || pf2==NULL) { printf("ERROR de acceso a ficheros. %s",msg); getch(); exit(1); } fseek(pf1,0,SEEK_END); numdatos = ftell(pf1)/ANCHO; //averiguar el n de registros que tenemos printf("Hay %d palabras existentes en disco. %s", numdatos, msg); getch(); do { menu = menudicc(); //llamada a la funcin de men del programa switch (menu) { case 1: anadir(); break; case 2: modificar(); break; case 3: eliminar(); break; case 4: listar(); break; case 5: ordenar(pf1,pf2); break; case 6: ordenar(pf2,pf1); break; case 7: buscar(pf1,pf2,1); printf("\n%s",msg); getch(); break; case 8: buscar(pf2,pf1,0); printf("\n%s",msg); getch(); break; } clearerr(pf1); clearerr(pf2); //desactivar marcas feof y ferror } while (menu>0); fclose(pf1); fclose(pf2); } int menudicc(void) //muestra en pantalla el men y retorna la tecla numrica pulsada { int m; system("cls"); setpos(8,30); printf("1. Aadir Palabras."); setpos(9,30); printf("2. Modificar Palabra."); setpos(10,30); printf("3. Eliminar Palabra."); setpos(11,30); printf("4. Listar Diccionario."); setpos(12,30); printf("5. Ordenar en Espaol."); setpos(13,30); printf("6. Ordenar en Ingles."); setpos(14,30); printf("7. Buscar en Espaol."); setpos(15,30); printf("8. Buscar en Ingles."); setpos(16,30); printf("0. Salir."); setpos(19,30); printf("Elija Opcion:"); do { setpos(19,44); m = getche()-'0'; } while (m<0 || m>8); system("cls"); return m; } void anadir(void) //Aade parejas de palabras al final del diccionario { int linea=3,i; char esp[ANCHO],ing[ANCHO]; fseek(pf1,0,SEEK_END); fseek(pf2,0,SEEK_END); //Apuntadores L/E al final del fichero printf("Introduzca parejas de palabras (VACIO = Terminar)..."); while (1) {

setpos(linea,1); printf("Palabra Espaola: "); setpos(linea,41); printf("Palabra Inglesa: "); leecad(linea,19,esp); if (strcmp(esp,"")==0) break; //lee palabra espaola y mira si est vaca leecad(linea,58,ing); if (strcmp(ing,"")==0) break; //lee palabra inglesa y comprueba si est vaca rellenablancos(esp); fwrite(esp,ANCHO,1,pf1); //graba la palabra espaola en minsculas rellenablancos(ing); fwrite(ing,ANCHO,1,pf2); //graba la palabra inglesa en minsculas linea++; if (linea==25) { linea=1; system("cls"); } //evita sobrepasar la linea 25 de pantalla } } int buscar(FILE *p1, FILE *p2, int espanol) //Busca una palabra, bien en espaol o en ingls { int ret=0; char cad[ANCHO],esp[ANCHO],ing[ANCHO]; printf("Introduzca palabra %s: ",espanol?"espaola":"inglesa"); leecad(1,41,cad); //tomar por teclado la palabra buscada if (strcmp(cad,"")!=0) //que no est vaca { strlwr(cad); //pasar la palabra buscada a minsculas printf("\nBuscando..."); rewind(p1); //buscar desde el principio del fichero fread(esp,ANCHO,1,p1); //leer registro en la cadena esp while (!feof(p1) && !ferror(p1)) //bucle de bsqueda secuencial { if (strcmp(cad,esp)==0) break; //comparar esp con la cadena buscada fread(esp,ANCHO,1,p1); } setpos(4,1); if (ferror(p1)) { printf("ERROR de lectura en fichero. %s",msg); getch(); } else if (feof(p1)) { printf("ERROR: Palabra no encontrada. %s",msg); getch(); } else { fseek(p1,-ANCHO,SEEK_CUR); //retroceder L/E un registro en el fichero de bsqueda fseek(p2,ftell(p1),SEEK_SET); //colocar en la misma posicin el L/E del 2 fichero fread(ing,ANCHO,1,p2); //leer registro del 2 fichero en la cadena ing fseek(p2,-ANCHO,SEEK_CUR); //retroceder L/E un registro en el 2 fichero if (espanol) //presentar ambas palabras en pantalla printf("Palabra Espaola: %s\t\tPalabra Inglesa: %s\n",esp,ing); else printf("Palabra Inglesa: %s\t\tPalabra Espaola: %s\n",esp,ing); ret=1; } } return ret; //retorna 1 si encontr la palabra buscada, 0 en caso contrario } void modificar(void) //modifica una pareja de palabras (espaola, inglesa o ambas) { char cad[ANCHO]; if (buscar(pf1, pf2, 1)) //llama a la funcin buscar para localizar la palabra espaola a modificar { setpos(6,1); printf("Nueva Palabra Espaola:"); leecad(6,25,cad); //pide nueva palabra por teclado if (strcmp(cad,"")!=0) //si no est vaca { rellenablancos(cad); fwrite(cad,ANCHO,1,pf1); } //graba la palabra en minsculas sin basura setpos(7,1); printf("Nueva Palabra Inglesa:"); leecad(7,25,cad); //pide la nueva palabra por teclado if (strcmp(cad,"")!=0) //si no esta vaca { rellenablancos(cad); fwrite(cad,ANCHO,1,pf2); } //graba la palabra en minsculas sin basura } }

void eliminar(void) //elimina una pareja de palabras, grabando un "nulo" como byte inicial { char rsp; if (buscar(pf1, pf2, 1)) //llama a la funcin buscar para localizar la palabra espaola a eliminar { setpos(6,1); printf("Eliminar Palabra. SEGURO? (S/N): "); //pedir confirmacin de eliminacin do rsp=toupper(getch()); while (rsp!='S' && rsp!='N'); printf("%c\n",rsp); //mostrar respuesta en la pantalla if (rsp=='S') //si respondimos que SI { fputc('\0',pf1); fputc('\0',pf2); //poner marca de fin de cadena como byte 1 de ambos registros } } } void listar(void) //muestra en pantalla todo el diccionario { int linea=3; //indicador de linea de pantalla char esp[ANCHO],ing[ANCHO],car=0; printf("LISTADO DEL DICCIONARIO:"); rewind(pf1); rewind(pf2); //desde el principio de ambos ficheros fread(esp,ANCHO,1,pf1); fread(ing,ANCHO,1,pf2); //leer registro en ambos ficheros while (!feof(pf1) && !ferror(pf1) && !feof(pf2) && !ferror(pf2) && car!=27) //bucle secuencial { if (strcmp(esp,"")!=0) //si no es un registro "eliminado" { setpos(linea,1); printf("Palabra Espaola: %s",esp); //imprimir palabra espaola setpos(linea,41); printf("Palabra Inglesa: %s",ing); //imprimir palabra inglesa linea++; //incrementar contador de lneas de pantalla } if (linea==24) //si es la linea 24 esperamos tecla y borramos pantalla { printf("\n%s (ESC=Fin)",msg); car=getch(); linea=1; system("cls"); } fread(esp,ANCHO,1,pf1); fread(ing,ANCHO,1,pf2); //leer registro en ambos ficheros } if (car!=27) //si la tecla pulsada fue ESC { printf("\n%s",msg); getch(); } //ultima pulsacion de tecla y finalizamos } void ordenar(FILE *p1,FILE *p2) //ordena alfabticamente, bien en espaol o en ingles (no ambas) { //ordena por el fichero maestro "p1" pasado como primer argumento int x,j,cambio,numdatos; char cad1[ANCHO],cad2[ANCHO]; printf("Ordenando...\n"); fseek(p1,0,SEEK_END); numdatos = ftell(p1)/ANCHO; //averiguar el n de registros que hay for (x=numdatos-1; x>0; x--) //metodo de ordenacin de "la burbuja" { cambio=0; //indicador de "intercambio realizado" for (j=0; j<x; j++) //j indica el n de registro actual del fichero maestro { fseek(p1,j*ANCHO,SEEK_SET); //colocar L/E del fichero maestro al principio del registro j fread(cad1,ANCHO,1,p1); //leemos en cad1 el registro j, y en cad2 el j+1 fread(cad2,ANCHO,1,p1); if (strcmp(cad1,cad2) > 0) //si cad1 es mayor alfabticamente que cad2 { cambio=1; fseek(p1,j*ANCHO,SEEK_SET); //volver a colocar L/E del fichero maestro en el registro j fwrite(cad2,ANCHO,1,p1); //grabamos los dos registros intercambiados (cad2+cad1)

fwrite(cad1,ANCHO,1,p1); fseek(p2,j*ANCHO,SEEK_SET); //colocar L/E del 2 fichero en registro j fread(cad1,ANCHO,1,p2); //leemos en cad1 el registro j, y en cad2 el j+1 fread(cad2,ANCHO,1,p2); fseek(p2,j*ANCHO,SEEK_SET); //volver a colocar L/E del 2 fichero en el registro j fwrite(cad2,ANCHO,1,p2); //grabamos los dos registros intercambiados (cad2+cad1) fwrite(cad1,ANCHO,1,p2); } } if (!cambio) break;

//si no ha habido ningn cambio, terminamos } printf("Ordenacin realizada. %s", msg); getch(); } void leecad(int fila,int columna,char *cadena) { //lee por teclado la "cadena", mostrandolo en la posicin de pantalla dada por fila y columna setpos(fila, columna); fgets(cadena, ANCHO, stdin); //N mximo de caracteres ledos = ANCHO-1 if (cadena[strlen(cadena)-1]=='\n') //eliminar el caracter \n final cadena[strlen(cadena)-1] = 0; } void rellenablancos(char *cad) //rellena con blancos finales la cadena para quitar posible "basura" { int i; strlwr(cad); //convierte texto a minusculas for (i=strlen(cad)+1; i<ANCHO; i++) cad[i]=' '; //rellena con blancos }

10. ESTRUCTURAS DINMICAS


CONCEPTOS GENERALES: Las estructuras dinmicas son variables de memoria que cambian de tamao a lo largo de la ejecucin del programa. Estan formadas por elementos de memoria aislados conectados unos con otros mediante punteros. Existir un puntero principal externo que apunte al comienzo de esta cadena de elementos. Cada elemento de memoria se va creando mediante asignacin dinmica de memoria, con la funcin malloc. Al finalizar el programa, debern ser todos eliminados mediante la funcin free. Las estructuras dinmicas ms utilizadas son: las listas lineales, las pilas, las colas, las listas doblemente enlazadas, las listas circulares y los rboles binarios. LISTAS LINEALES: Las listas lineales estan formadas por elementos de tipo variable struct no consecutivos en la memoria. Cada elemento contendr uno o ms campos de datos, y adems al menos un campo de tipo puntero que servir para apuntar al siguiente elemento de la cadena (contendr la direccin de memoria donde se encuentra almacenado dicho elemento siguiente). El ltimo elemento de la cadena contendr un valor NULL en dicho puntero, indicando que ya no existe ningn elemento siguiente. Existir un puntero externo que apuntar en todo momento al primer elemento de la cadena. Si cada elemento tiene un nico puntero que apunta al siguiente elemento de la cadena, la lista se denomina "simplemente enlazada". Si cada elemento tiene dos punteros, uno al elemento siguiente de la cadena y otro al elemento anterior, se denomina "doblemente enlazada". En una lista simplemente enlazada, la exploracin de la cadena de elementos se realizar de forma secuencial nicamente en una direccin, desde el primer elemento hasta el ltimo. En estas listas lineales se pueden realizar tareas tales como aadir nuevos elementos en cualquier punto de la cadena, eliminarlos, recorrerlos secuencialmente, buscar un dato en su interior, etc. CREACIN DE UNA LISTA LINEAL SIMPLEMENTE ENLAZADA:
EJEMPLO-01: #include <stdio.h> #include <stdlib.h> // Declaraciones globales typedef struct s { int num1; float num2; struct s *siguiente; } elemento; elemento *lista = NULL;

//declaracin del tipo struct, elemento de la cadena

//declaracin del puntero maestro global

void insertar(int x, float y); //Funciones prototipo void imprimir(void); void borrar(void); main( ) { int x; float y; printf("Introduzca datos: "); while (1)

{ printf("\nx = "); if (scanf("%d",&x)!=1) break; printf("y = "); if (scanf("%f",&y)!=1) break; insertar(x,y); } printf("\nLos datos introducidos son:\n"); imprimir(); borrar(); } void insertar(int x, float y) //Introducir nuevo elemento al principio de la lista { elemento *q = (elemento *)malloc(sizeof(elemento)); if (q==NULL) { printf("Falta memoria"); getch(); } else { q->num1=x; q->num2=y; q->siguiente=lista; lista=q; } } void imprimir(void) //Escribir todos los elementos de la lista { elemento *q=lista; while (q!=NULL) { printf("x = %d, y = %g\n",q->num1,q->num2); q = q->siguiente; } } void borrar(void) //Borrar todos los elementos de la lista { elemento *q=lista; while (q!=NULL) { lista = lista->siguiente; free(q); q=lista; } }

INSERCIN DE UN ELEMENTO EN MITAD DE LA LISTA:


EJEMPLO-02: // Insertar un nuevo elemento despus de uno dado por el puntero "p" void insertarsig(elemento *p, int x, float y) { elemento *q = NuevoElemento(); if (q!=NULL) { q->num1=x; q->num2=y; q->siguiente=p->siguiente; p->siguiente=q; } } //Insertar un nuevo elemento antes de uno dado por el puntero "p" void insertarant(elemento *p, int x, float y) { elemento *q = NuevoElemento(); if (q) { *q = *p; p->num1=x; p->num2=y; p->siguiente=q; } } //Crear nuevo elemento de la lista elemento *NuevoElemento(void) { elemento *q = (elemento *)malloc(sizeof(elemento)); if (q==NULL) { printf("Falta memoria"); getch(); } return q; }

ELIMINACIN DE ELEMENTOS DE LA LISTA:


EJEMPLO-03: //Eliminar un elemento dado, si no es el ltimo de la lista void eliminar(elemento *p)

{ elemento *q = p->siguiente; if (q!=NULL) { *p = *q; free(q); } } //Eliminar el elemento siguiente a uno dado void eliminarsig(elemento *p) { elemento *q = p->siguiente; if (q) { p->siguiente = q->siguiente; free(q); } }

BSQUEDA DE ELEMENTOS EN LA LISTA:


EJEMPLO-04: //Buscar un elemento de la lista void buscar(void) { int num; elemento *q=lista; printf("Dame valor de X a buscar:"); if (scanf("%d", &num)!=1) return; while (q!=NULL && q->num1!=num) q=q->siguiente; if (q==NULL) printf("Dato no encontrado"); else printf("El valor de Y asociado a X=%d es: %g", num, q->num2); }

11. ESTRUCTURAS DINMICAS (II)


PILAS: Una pila es un sistema de almacenamiento de datos en el cual el ltimo dato que lleg por la entrada es el primero que sale (parecido al comportamiento de una pila de platos). En informtica se denomina almacenamiento tipo LIFO (Last Input First Output). Podemos construir una pila mediante una lista lineal simplemente enlazada, en la cual todas las inserciones de nuevos datos y todas las extracciones se realizan por un mismo extremo de la lista (el de comienzo de la lista enlazada), llamado cima de la pila. Para controlar la pila basta con tener un puntero maestro externo que apunte a dicho extremo de la lista. La operacin de introduccin de un dato en la pila suele denominarse push, y la operacin de extraccin de un dato suele denominarse pop. Al extraer un dato de la lista (el primero de la cadena), tal dato es eliminado y el puntero maestro pasa a apuntar al siguiente elemento de la cadena, que se convierte en la nueva cima de la pila. Cuando el puntero maestro vale NULL, indica que la pila est vaca.
EJEMPLO-01: /* Creacion de una pila mediante una lista enlazada */ #include <stdio.h> #include <stdlib.h> /* Funciones prototipo */ void push(char dato); char pop(void); void *NuevoElem(size_t numbytes); /* Declaraciones globales */ typedef struct n { char num; struct n *siguiente; } numero; numero *pila = NULL; /* Programa principal */ main() { char m; push(13); push(22); push(33); m = pop(); printf("%d ",m); m = pop(); printf("%d ",m); while (pila!=NULL) m = pop(); //Vaciar toda la pila } /* Introducir dato en la pila */ void push(char dato) { numero *q = (numero *)NuevoElem(sizeof(numero)); if (q!=NULL) { q->siguiente = pila; q->num = dato; pila = q; } } /* Extraer dato de la pila */ char pop(void) { numero *q; char x; if (pila==NULL) { printf("Lista vaca"); getch(); return EOF; }

x = pila->num; q = pila; pila = pila->siguiente; free(q); return x; } /* Crear nuevo elemento de la pila */ void *NuevoElem(size_t numbytes) { void *q = (void *)malloc(numbytes); if (q==NULL) { printf("Falta memoria"); getch(); } return q; }

COLAS: Una cola es un sistema de almacenamiento de datos en el cual el primer dato que lleg por la entrada es el primero que sale (parecido al comportamiento de una cola en un cine). En informtica se denomina almacenamiento tipo FIFO (First Input First Output). Podemos construir una cola mediante una lista lineal simplemente enlazada, en la cual las inserciones de nuevos datos se realizan por un extremo de la lista y las extracciones se realizan por el extremo contrario de la lista. Para controlar la cola se necesitan dos punteros maestros, uno que apunte al extremo de entrada de la lista enlazada y otro que apunte al extremo de salida. La operacin de introduccin de un dato en la cola suele denominarse push, y la operacin de extraccin de un dato suele denominarse pop. Al extraer un dato de la cola, tal dato es eliminado y el puntero maestro pasa a apuntar al siguiente elemento de la cadena. Cuando ambos punteros maestros valen NULL, significa que la cola est vaca.
EJEMPLO-02: /* Creacion de una cola mediante una lista enlazada */ #include <stdio.h> #include <stdlib.h> /* Prototipos de funciones */ void push(char dato); char pop(void); void *NuevoElem(size_t numbytes); /* Declaraciones globales */ typedef struct n { char num; struct n *siguiente; } numero; numero *inicio = NULL, *final = NULL; /* Programa principal */ main() { char m; push(13); push(22); push(33); m = pop(); printf("%d ",m); m = pop(); printf("%d ",m); while (inicio!=NULL) m = pop(); // Vaciar toda la cola } /* Introducir dato en la cola */ void push(char dato) { numero *q = (numero *)NuevoElem(sizeof(numero)); if (q!=NULL)

{ if (final!=NULL) final->siguiente = q; final = q; final->num = dato; final->siguiente = NULL; if (inicio==NULL) inicio = q; } } /* Extraer dato de la cola */ char pop(void) { numero *q; char x; if (inicio==NULL) { printf("Cola vaca"); getch(); return EOF; } x = inicio->num; q = inicio; inicio = inicio->siguiente; free(q); if (inicio==NULL) final = NULL; return x; } /* Crear nuevo elemento de la cola */ void *NuevoElem(size_t numbytes) { void *q = (void *)malloc(numbytes); if (q==NULL) { printf("Falta memoria"); getch(); } return q; }

LISTAS CIRCULARES: Una lista circular es una lista lineal en la cual el ltimo elemento de la cadena apunta al primer elemento de la misma, formando un anillo de circulacin continua. Para controlar esta lista se necesita un puntero maestro externo que apunte a cualquier elemento del anillo.
EJEMPLO-03: /* Creacion de una lista circular mediante una lista enlazada */ #include <stdio.h> #include <stdlib.h> /* Prototipos de funciones */ void *NuevoElem(size_t numbytes); /* Declaraciones globales */ typedef struct n { int num; struct n *siguiente; } numero; numero *lista = NULL; /* Programa principal */ main() { numero *q; int dato; printf("Introduzca nmeros (CTRL+Z = Fin)\n\n"); while (1) { printf("Numero: "); scanf("%d",&dato); if (feof(stdin)) break; q = (numero *)NuevoElem(sizeof(numero)); q->num = dato; if (lista==NULL) { q->siguiente = q; lista = q;} else { q->siguiente = lista->siguiente; lista->siguiente = q; } } printf("\nLos elementos de la lista son:\n"); q = lista; do

{ printf("%d ",q->num); q = q->siguiente; } while (q!=lista); printf("\nEliminando la lista...\n"); while (lista!=NULL) { q = lista->siguiente; if (q==lista) { free(lista); lista = NULL; } else { *lista = *q; free(q); } } printf("Lista eliminada."); } /* Creacion de un nuevo elemento de la lista */ void *NuevoElem(size_t numbytes) { void *q = (void *)malloc(numbytes); if (q==NULL) { printf("Falta memoria"); getch(); } return q; }

LISTAS DOBLEMENTE ENLAZADAS: Una lista doblemente enlazada es aquella en la que cada elemento de la cadena tiene un puntero que apunta al elemento siguiente, y otro puntero que apunta al elemento anterior. Este tipo de listas permite su recorrido en las dos direcciones posibles: del primero al ltimo, o bien del ltimo al primero. Para controlar estas listas se necesita un puntero maestro externo que apunte al primer elemento de la lista enlazada.
EJEMPLO-04: /* Creacion de una lista doblemente enlazada */ #include <stdio.h> #include <stdlib.h> /* Prototipos de funciones */ void *NuevoElem(size_t numbytes); /* Declaraciones globales */ typedef struct n { struct n *anterior; int num; struct n *siguiente; } numero; numero *lista = NULL; /* Programa principal */ main() { numero *q; int dato; printf("Introduzca nmeros (CTRL+Z = Fin)\n\n"); while (1) { printf("Numero: "); if (scanf("%d",&dato)==EOF) break; q = (numero *)NuevoElem(sizeof(numero)); q->num = dato; q->siguiente = lista; q->anterior = NULL; if (lista!=NULL) lista->anterior = q; lista = q; } printf("\nLos datos de la lista son:\n"); q = lista; while (q!=NULL) { printf("%d ",q->num); q = q->siguiente; }

printf("\nEliminando la lista...\n"); while (lista!=NULL) { q = lista; lista = lista->siguiente; free(q); } printf("Lista eliminada."); } /* Creacion de un nuevo elemento de la lista */ void *NuevoElem(size_t numbytes) { void *q = (void *)malloc(numbytes); if (q==NULL) { printf("Falta memoria"); getch(); } return q; } /* Eliminacin de un elemento cualquiera de la lista */ void eliminar(numero *p) { numero *ant = p->anterior, *sig = p->siguiente; if (ant!=NULL) ant->siguiente = p->siguiente; if (sig!=NULL) sig->anterior = p->anterior; if (p==lista) lista = p->siguiente; free(p); }

12. RBOLES BINARIOS


CONCEPTOS GENERALES: Un rbol es una estructura de almacenamiento de datos formada por un conjunto de "nodos" unidos entre s mediante "ramas". Cada nodo puede almacenar uno o ms datos, de los cuales uno es el principal llamado "clave". Cada nodo almacena tambin tantos punteros como ramas salgan de l, punteros que apuntan a otros nodos del rbol. El nodo primario del rbol se llama nodo "raz", y de l salen ramas que conectan con otros nodos, de los cuales salen ms ramas. Finalmente, un nodo del cual no sale ninguna rama se llama nodo "terminal". Un rbol se puede definir de forma recursiva como "el conjunto formado por un nodo raz del cual parten ramas que conectan con otros nodos, que a su vez son rboles". Es por ello que la forma ms sencilla de programar una estructura de rbol es mediante funciones recursivas. El nmero de ramas que salen de un nodo se denomina "grado" del nodo. La distancia en nmero de ramas que separa un nodo del nodo raz primario del rbol se denomina "nivel" del nodo. El mximo "nivel" alcanzado por alguno de los nodos del rbol se denomina "altura" o "profundidad" del rbol. RBOLES BINARIOS: Un rbol binario es aquel cuyos nodos tienen como mximo grado 2, es decir, que como mximo salen 2 ramas de cada nodo (pueden salir 2 ramas, una o ninguna). Se dice entonces que cada nodo tiene un "subrbol derecho" y un "subrbol izquierdo". Cada nodo puede programarse mediante una variable de tipo "struct" que contenga uno o ms campos de dato, y un par de campos de tipo puntero que apunten uno al nodo del subrbol izquierdo y otro al nodo del subrbol derecho: struct datos { int datoclave; struct datos *izqdo; struct datos *dcho; }; Un valor NULL en alguno de dichos dos punteros indica que no existe el subrbol correspondiente. Un nodo "terminal" ser aquel con valor NULL en ambos punteros. El rbol se gobierna mediante un puntero externo que apunta al nodo raz primario del rbol: struct datos *raiz; Si el puntero raz vale NULL significa que el rbol est vaco y no contiene todava ningn nodo. RBOLES BINARIOS DE BSQUEDA: Un "rbol binario de bsqueda" es aquel cuyos nodos se almacenan de forma ordenada de acuerdo con la siguiente regla: "para todo nodo N, todas las claves de los nodos del subrbol izquierdo son menores que la clave del nodo N, y todas las claves de los nodos del subrbol derecho son mayores que la clave del nodo N". El rbol binario de bsqueda es el tipo de almacenamiento ms til cuando de partida no se conoce la cantidad de datos a almacenar, los datos se reciben en orden aleatorio y deben ser almacenados de forma ordenada, y la velocidad de bsqueda de los datos almacenados debe ser elevada.

EJEMPLO-01: /* Programa que almacena los nmeros recibidos por el teclado y cuenta cuntas veces repetidas se ha recibido cada uno */ #include <stdio.h> #include <stdlib.h> /* Declaraciones globales */ typedef struct datos //tipo struct de cada nodo del rbol { int clave, contador; struct datos *izqdo, *dcho; } nodo; nodo *raiz=NULL; //puntero al nodo raz primario del rbol /* funciones prototipo */ void creararbol(void); nodo *anadir(int num, nodo *r); void borrararbol(nodo *r); /* Programa principal */ main( ) { system(cls); creararbol( ); //creacin del rbol /* Tareas a realizar con el rbol */ borrararbol(raiz); //eliminacin de todos los nodos del rbol } void creararbol(void) { int numero,c; while (1) { printf(Nmero: ); c=scanf(%d,&numero); if (c==0) continue; if (c==EOF) break; raz = anadir(numero, raiz); }

//bucle para creacin del rbol //leer nmero desde el teclado //si se ha tecleado un dato incorrecto //si se ha pulsado CTRL+Z //insertar nuevo nmero en el rbol

nodo *anadir(int num, nodo *r) //funcin recursiva { if (r==NULL) { r = (nodo *)malloc(sizeof(nodo)); //se crea nuevo nodo y se carga de datos if (r==NULL) { printf (Falta Memoria.); getch(); } else { r->clave = num; r->contador = 1; r->izqdo = r->dcho = NULL; } } else { if (num < r->clave) //si el nmero es menor que la clave del nodo actual r->izqdo = anadir(num, r->izqdo); else if (num > r->clave) //si el nmero es mayor que la clave del nodo actual r->dcho = anadir(num, r->dcho); else //si el nmero es igual que la clave del nodo actual r->contador++; } return r; } void borrararbol(nodo *r) //funcin recursiva { if (r!=NULL) { borrararbol(r>izgdo); borrararbol(r->dcho); free(r); } }

RECORRIDO DE RBOLES BINARIOS: Una tarea habitual con los rboles binarios, una vez construdos y cargados de datos, es "recorrerlos", es decir, visitar todos y cada uno de los nodos del rbol, visitando cada uno una nica vez. Existen tres modos diferentes de recorrido estandar de un rbol binario: en PreOrden, en InOrden y en PostOrden. Recorrido en PreOrden (secuencia RID): para todo nodo, se visita primero el nodo raz (R), despus el subrbol izquierdo (I), y despus el subrbol derecho (D). Cabe tambin la variante de recorrido con secuencia RDI (Raz-Derecho-Izquierdo). Recorrido en InOrden (secuencia IRD): para todo nodo, se visita primero el subrbol izquierdo (I), luego el propio nodo raz (R), y despus el subrbol derecho (D). Este es el tipo de recorrido necesario para obtener los datos ordenados en orden ascendente. Cabe tambin la variante de recorrido con secuencia DRI (Derecho-Raz-Izquierdo), que da lugar a un recorrido ordenado en orden descendente. Recorrido en PostOrden (secuencia IDR): para todo nodo, se visita primero el subrbol izquierdo (I), luego el subrbol derecho (D) y finalmente el propio nodo raz (R). Cabe tambin la variante de recorrido con secuencia DIR (Derecho-Izquierdo-Raz). Este es el tipo de recorrido necesario para eliminar todo el rbol, antes de terminar el programa.
EJEMPLO-02: /* Recorridos en un arbol binario */ #include <stdio.h> #include <stdlib.h> /* Declaraciones globales */ typedef struct datos //tipo struct de cada nodo del rbol { int clave, contador; struct datos *izqdo, *dcho; } nodo; nodo *raiz=NULL; //puntero al nodo raz primario del rbol /* funciones prototipo */ void creararbol(void); nodo *anadir(int num, nodo *r); void preorden(nodo *r); void inorden(nodo *r); void postorden(nodo *r); void borrararbol(nodo *r); /* Programa principal */ main( ) { system(cls); creararbol( ); //creacin del rbol /* Tareas a realizar con el rbol */ printf(\n\nRecorrido en PREORDEN:\n); preorden(raiz); printf(\n\nRecorrido en INORDEN:\n); inorden(raiz); printf(\n\nRecorrido en POSTORDEN:\n); postorden(raiz); borrararbol(raiz); //eliminacin de todos los nodos del rbol } void preorden(nodo *r) //funcin recursiva { if (r!=NULL) { printf("%d ",r->clave); preorden(r->izqdo); preorden(r->dcho); } }

void inorden(nodo *r) //funcin recursiva { if (r!=NULL) { inorden(r>izqdo); printf(El n %d se ha introducido %d veces\n,r->clave,r->contador); inorden (r>dcho); } } void postorden(nodo *r) //funcin recursiva { if (r!=NULL) { postorden(r->izqdo); postorden(r->dcho); printf(%d ,r->clave); } }

BSQUEDA EN UN RBOL BINARIO DE BSQUEDA: La bsqueda de un dato en un rbol binario de bsqueda se realiza fcilmente de forma recursiva, aplicando la definicin de este tipo de rboles.
EJEMPLO-03: /* Bsqueda en un arbol binario de bsqueda */ #include <stdio.h> #include <stdlib.h> typedef struct datos //tipo struct de cada nodo del rbol { int clave, contador; struct datos *izqdo, *dcho; } nodo; nodo *raiz=NULL; //puntero al nodo raz primario del rbol void creararbol(void); nodo *anadir(int num, nodo *r); nodo *buscar(int dato, nodo *r); void borrararbol(nodo *r); main( ) { int clv=0; nodo *p; system(cls); creararbol( ); /* Tareas a realizar con el rbol */ printf("Indique la clave del nodo a localizar: "); scanf("%d", &clv); p = buscar(clv, raiz); if (p==NULL) printf("No existe esta clave en el rbol"); else printf("La clave %d ha sido introducida %d veces", p->clave, p->contador); borrararbol(raiz); } nodo *buscar(int dato, nodo *r) //funcin recursiva { if (r==NULL) return r; //dato no existe else if (dato > r->clave) return buscar(dato, r->dcho); //buscar en subarbol derecho else if (dato < r->clave) return buscar(dato, r->izqdo); //buscar en subarbol izquierdo else return r; //dato encontrado }

13. ALGORITMOS DE ORDENACIN DE DATOS


CONCEPTOS GENERALES: La clasificacin u ordenacin de los datos de una lista de menor a mayor (o viceversa) es un proceso sumamente frecuente en cualquier aplicacin informtica. Existen varios algoritmos de ordenacin posibles. El mejor ser aquel que consiga la ordenacin en el menor tiempo posible, es decir, el que realice la tarea ms rpidamente. Cuanto menor sea el nmero de intercambios de datos realizados, mayor ser la velocidad del proceso. Estudiaremos estos mtodos aplicndolos a un array numrico de N elementos, cuyos datos queremos ordenar de menor a mayor (el elemento menor en el subndice [0], y el mayor en el subndice [N-1]). MTODO DE LA BURBUJA: Consiste en dar N-1 pasadas sucesivas por el array de datos a ordenar. En cada pasada, se comienza desde el elemento de subndice ms bajo [0], comparndolo con el elemento de subndice siguiente [1]. Si ambos elementos estan desordenados (el primero es mayor que el segundo), se intercambian. Si estan bien ordenados, se dejan como estan. Luego se comparan los elementos de subndice [1] y [2], luego [2] con [3], y as sucesivamente hasta comparar el dato penltimo con el ltimo. Al final de esta primera pasada, el ltimo elemento del array quedar bien colocado (contendr el dato mayor de todo el array). Luego se repite el proceso en una segunda pasada, pero trabajando slo con el resto del array menos en ltimo elemento. Al terminar esta 2 pasada, el penltimo elemento del array quedar bien colocado. Se contina con el resto de pasadas hasta completar N-1 vueltas. Existe una versin mejorada de este algoritmo, en la cual se incluye un detector que nos informa si NO se ha realizado ninguna operacin de intercambio a lo largo de la pasada n-sima en la que nos encontramos. Si esto sucede significa que el array est ya completamente ordenado y no tenemos que continuar con las siguientes pasadas. Podremos dar por concluido el proceso anticipadamente, ganando en velocidad de proceso.
EJEMPLO-01: // Metodo de la Burbuja #include <stdio.h> #define N 4 //Nmero de elementos del array main() { int a[N]={17,10,12,5}; //array a ordenar int n; //n de pasadas que faltan por realizar int cambio=1; //detector de intercambio en una pasada int aux; //auxiliar para realizar intercambio int i; for (n=N-1; n>0 && cambio; n--) //bucle de pasadas que faltan { cambio=0; //poner cambio a falso for (i=0; i<n; i++) if (a[i]>a[i+1]) //si estan desordenados... { aux=a[i]; a[i]=a[i+1]; a[i+1]=aux; //intercambiar cambio=1; //poner cambio a cierto } } printf("El array esta ordenado"); }

MTODO DE SELECCIN: Consiste en dar N-1 pasadas sucesivas por el array de datos a ordenar. En cada pasada, se localiza desde el elemento de subndice ms bajo [0] dnde se encuentra el elemento mayor del array. Una vez localizado, se intercambia con el ltimo elemento del array, quedando este ya bien colocado. Luego se repite el proceso en una segunda pasada, pero trabajando slo con el resto del array menos en ltimo elemento. Al terminar esta 2 pasada, el penltimo elemento del array quedar bien colocado. Se contina con el resto de pasadas hasta completar N-1 vueltas.
EJEMPLO-02: // Metodo de Seleccin #include <stdio.h> #define N 4 //Nmero de elementos del array main() { int a[N]={17,10,12,5}; //array a ordenar int n; //n de pasada actual int max; //indice del dato mayor int aux; //auxiliar para realizar intercambio int i; for (n=N-1; n>0; n--) //bucle de pasadas que faltan { max=0; //suponemos el mayor en la posicin 0 for (i=1; i<=n; i++) if (a[i]>a[max]) max=i; //detectamos indice del dato mayor if (max!=n) { aux=a[n]; a[n]=a[max]; a[max]=aux; } //intercambiar a[n] con a[max] } printf("El array esta ordenado"); }

MTODO DE INSERCIN: Consiste en dar N-1 pasadas sucesivas por el array de datos a ordenar. En la primera pasada se ordenan los dos primeros elementos del array. En la segunda pasada se inserta el tercer elemento en el lugar adecuado entre los dos primeros, desplazando los elementos superiores. En la tercera pasada se inserta el cuarto elemento en el lugar adecuado entre los tres primeros, desplazando los elementos superiores. Se contina con el resto de pasadas hasta completar N-1 vueltas.
EJEMPLO-03: // Metodo de Insercin #include <stdio.h> #define N 4 //Nmero de elementos del array main() { int a[N]={17,10,12,5}; //array a ordenar int i; //indice del elemento actual a insertar int j; //indice de los elementos anteriores int aux; //guarda copia del elemento actual a insertar for (i=1; i<N; i++) //bucle de elementos actuales a insertar { aux=a[i]; //copiamos elemento actual a insertar j = i-1; //empezamos insercin desde el elemento anterior while (j>=0 && aux<a[j]) { a[j+1] = a[j]; j--; } //desplazamos elementos hacia la derecha a[j+1]=aux; //insertamos dato en el lugar adecuado } printf("El array esta ordenado"); }

MTODO RECURSIVO QUICKSORT: Es el mtodo ms rpido de ordenacin, con mucha diferencia. Al ser un mtodo recursivo, puede requerir una capacidad elevada de la "pila" del sistema (zona de memoria donde se almacenan las variables locales), si la cantidad de elementos a ordenar es elevada. Se selecciona como referencia el valor del elemento mitad del array a ordenar. Se colocan todos los valores menores de dicho valor de referencia en las posiciones bajas del array, y todos los elementos mayores o iguales en las posiciones altas del array. El array queda dividido en dos partes (datos menores y datos mayores o iguales). Se repite el proceso con la parte baja del array (la que contiene aquellos elementos menores), y luego con la parte alta del array (la que contiene aquellos elementos mayores o iguales), dividiendo en cada paso cada subarray en dos nuevas partes. La recursin termina cuando la porcin de array a ordenar consta de un nico elemento.
EJEMPLO-04: // Metodo Quicksort #include <stdio.h> #define N 4 //Nmero de elementos del array void qsort(int *lista, int numelem); //declaraciones prototipo void qs(int *lista, int posinf, int possup); main() { int a[N]={17,10,12,5}; //array a ordenar qsort(a, N); //llamada a la funcin de ordenacin printf("El array esta ordenado"); } void qsort(int *lista, int numelem) //funcin de ordenacin { qs(lista, 0, numelem-1); } //llamada a la funcin recursiva void qs(int *lista, int posinf, int possup) //funcin recursiva { int izq; //indice del elemento izquierdo a intercambiar int der; //indice del elemento derecho a intercambiar int mitad; //valor del elemento mitad del array int aux; //auxiliar para realizar intercambio izq=posinf; //izq comienza desde el extremo inferior del array der=possup; //der comienza desde el extremo superior del array mitad = lista[(izq+der)/2]; do { //bucle para aumentar "izq" hasta encontrar dato mayor o igual que "mitad" while (lista[izq]<mitad && izq<possup) izq++; //bucle para disminuir "der" hasta encontrar dato menor o igual que "mitad" while (lista[der]>mitad && der>posinf) der--; if (izq<=der) { if (izq!=der) { aux=lista[izq]; lista[izq]=lista[der]; lista[der]=aux; } //intercambiar datos "izq" y "der" izq++; der--; } } while (izq<=der); //continuar bsqueda hasta que izq>der //repetir proceso con la parte baja del array if (posinf<der) qs(lista, posinf, der); //repetir proceso con la parte alta del array if (possup>izq) qs(lista, izq, possup); }

EJEMPLO-05:

COMPARATIVA DE ALGORITMOS: Al comprobar el funcionamiento de estos 4 algoritmos sobre un array de 10000 nmeros aleatorios de tipo float, ordenndolo 10 veces consecutivas, los resultados han sido los siguientes: Mtodo de la burbuja: 13950 milisegundos. Mtodo de seleccin: 8570 milisegundos. Mtodo de insercin: 5000 milisegundos. Mtodo Quicksort: 50 milisegundos.
EJEMPLO-06: #include <stdio.h> #include <time.h> #include <stdlib.h> #define N 10000 //Nmero de elementos del array long test(void (*pfunc)()); void burbuja(float *a, int numelem); void seleccion(float *a, int numelem); void insercion(float *a, int numelem); void qsort(float *lista, int numelem); void qs(float *lista, int posinf, int possup); float a[N]; //array a ordenar main() { printf("Tiempo por mtodo de la Burbuja: %ld ms\n", test(burbuja)); printf("Tiempo por mtodo de Seleccion: %ld ms\n", test(seleccion)); printf("Tiempo por mtodo de Insercion: %ld ms\n", test(insercion)); printf("Tiempo por mtodo Quicksort: %ld ms\n", test(qsort)); printf("Pulse una tecla..."); getch(); } long test(void (*pfunc)()) //devuelve milisegundos { int i,j; long tinic,trand; tinic=clock(); //tomar tiempo inicial de carga for (i=0; i<N; i++) a[i]=rand(); //cargar array con numeros aleatorios trand = clock()-tinic; //calcular tiempo total de carga tinic = clock(); //tomar tiempo inicial de ordenaciones for (j=0; j<10; j++) //bucle de 10 ordenaciones { for (i=0; i<N; i++) a[i]=rand(); //cargar array pfunc(a, N); //llamar a funcion de ordenacion } return clock()-tinic-10*trand; //retornar tiempo empleado en ordenaciones } void burbuja(float *a, int numelem) { int n,i; //n de pasadas que faltan por realizar int cambio=1; //detector de intercambio en una pasada float aux; //auxiliar para realizar intercambio for (n=numelem-1; n>0 && cambio; n--) //bucle de pasadas que faltan { cambio=0; //poner cambio a falso for (i=0; i<n; i++) if (a[i]>a[i+1]) //si estan desordenados... { aux=a[i]; a[i]=a[i+1]; a[i+1]=aux; //intercambiar cambio=1; //poner cambio a cierto } } }

void seleccion(float *a, int numelem) { int n,i; //n de pasada actual int max; //indice del dato mayor float aux; //auxiliar para realizar intercambio for (n=numelem-1; n>0; n--) //bucle de pasadas que faltan { max=0; //suponemos el mayor en la posicin 0 for (i=1; i<=n; i++) if (a[i]>a[max]) max=i; //detectamos indice del dato mayor if (max!=n) { aux=a[n]; a[n]=a[max]; a[max]=aux; } //intercambiar a[n] con a[max] } } void insercion(float *a, int numelem) { int i; //indice del elemento actual a insertar int j; //indice de los elementos anteriores float aux; //guarda copia del elemento actual a insertar for (i=1; i<N; i++) //bucle de elementos actuales a insertar { aux=a[i]; //copiamos elemento actual a insertar j = i-1; //empezamos insercin desde el elemento anterior while (j>=0 && aux<a[j]) { a[j+1] = a[j]; j--; } //desplazamos elementos hacia la derecha a[j+1]=aux; //insertamos dato en el lugar adecuado } } void qsort(float *lista, int numelem) //funcin de ordenacin { qs(lista, 0, numelem-1); //llamada a la funcin recursiva } void qs(float *lista, int posinf, int possup) //funcin recursiva { int izq; //indice del elemento izquierdo a intercambiar int der; //indice del elemento derecho a intercambiar float mitad; //valor del elemento mitad del array float aux; //auxiliar para realizar intercambio izq=posinf; //izq comienza desde el extremo inferior del array der=possup; //der comienza desde el extremo superior del array mitad = lista[(izq+der)/2]; do { //bucle para aumentar "izq" hasta encontrar dato mayor o igual que "mitad" while (lista[izq]<mitad && izq<possup) izq++; //bucle para disminuir "der" hasta encontrar dato menor o igual que "mitad" while (lista[der]>mitad && der>posinf) der--; if (izq<=der) { //intercambiar los datos de posicion "izq" y "der" if (izq<der) { aux=lista[izq]; lista[izq]=lista[der]; lista[der]=aux; } izq++; der--; } } while (izq<=der); //continuar bsqueda hasta que izq>der //repetir proceso con la parte baja del array if (posinf<der) qs(lista, posinf, der); //repetir proceso con la parte alta del array if (possup>izq) qs(lista, izq, possup); }

14. ALGORITMOS DE BSQUEDA DE DATOS


BSQUEDA SECUENCIAL: Es el mtodo de bsqueda mas sencillo pero menos eficiente. Se trata de comparar el valor buscado con todos y cada uno de los valores de la lista, del primero al ltimo, hasta que se encuentra coincidencia o se acaba la lista. Esta lista no necesita estar ordenada.
EJEMPLO-01: #include <stdio.h> #define N 500 int BusquedaSec(int a[ ], int numelem, int dato); main() { int a[N], dato=0, i; /* Proceso de carga de datos del array */ printf("Indique el dato a buscar: "); scanf("%d", &dato); i = BusquedaSec(a, N, dato); if (i==N) printf("Dato no encontrado"); else printf("Dato %d encontrado en la posicin %d", dato, i); } int BusquedaSec(int a[ ], int numelem, int dato) { int i; for (i=0; i<numelem && a[i]!=dato; i++); return i; }

BSQUEDA BINARIA: La bsqueda binaria es un mtodo eficiente que se puede aplicar nicamente a listas ordenadas. Consiste en comparar el dato buscado con el elemento central de la lista. Si el dato buscado es mayor que el central, se repite la misma bsqueda sobre la mitad superior de la lista. Si el dato buscado es menor que el central, se repite la busqueda sobre la mitad inferior de la lista. El proceso se repite hasta que la porcin de lista explorada tiene uno o ningn elemento. Se puede programar de forma recursiva (ya visto anteriormente) o de forma iterativa (mediante bucle).
EJEMPLO-02: #include <stdio.h> #define N 500 main() { int a[N], dato=0, i; /* Proceso de carga de datos del array y ordenacin ascendente de los mismos */ printf("Indique el dato a buscar: "); scanf("%d", &dato); i = BusquedaBin(a, N, dato); if (i==-1) printf("Dato no encontrado"); else printf("Dato %d encontrado en la posicin %d", dato, i); }

int BusquedaBin(int a[ ], int numelem, int dato) { int mitad, inf=0, sup=numelem-1; if (numelem<=0) return -1; do { mitad=(inf+sup)/2; if (dato>a[mitad]) inf=mitad+1; else sup=mitad-1; } while (a[mitad]!=dato && inf<=sup); if (a[mitad]==dato) return mitad; else return -1; }

ALGORITMOS HASH: Son mtodos de bsqueda muy rpida que permiten localizar los datos dentro de una amplia lista con un escaso nmero de accesos a la misma, sin que la lista tenga que estar ordenada. El mtodo general consiste en utilizar un array de punteros llamado matriz Hash, punteros que apuntarn a la ubicacin en memoria de cada elemento de datos que queramos almacenar mediante tcnicas de asignacin dinmica (funcin malloc). La bsqueda de los datos se realiza por medio de dicha matriz. El nmero de elementos de dicha matriz (L) debe ser mayor que el nmero mximo estimado de datos que queremos almacenar (N), al menos el doble, y debe ser un nmero primo. El n medio de accesos sobre la matriz para buscar un dato cualquiera viene dado por la frmula: (2-k)/(2-2k) donde k es el cociente N/L. Por ejemplo, si el n de elementos de la matriz es el doble del n de datos (k=1/2), el n medio de accesos es de slo 1,5 veces. En los datos a almacenar debe existir un campo Clave identificativo de cada dato. Esta Clave debe ser numrica y no repetible. Si la Clave es alfabtica, debe ser transformada a nmero no repetible mediante algn algoritmo. PROCEDIMIENTO DE ALMACENAMIENTO HASH: El procedimiento de almacenamiento (funcin HashIn()) consiste en obtener a partir de la Clave del dato a almacenar un nmero entero H mediante la frmula siguiente: H = Clave % L (resto de la divisin entre la Clave y el n de elementos de la matriz Hash). Este nmero H (valor de 0 a L-1) indica la posicin del elemento de la matriz de punteros Hash donde se anotar la direccin de memoria del dato a almacenar (direccin obtenida mediante malloc). Puede suceder colisin, cuando dos Claves diferentes dan lugar a un mismo nmero H. El nmero de colisiones es escaso si L es un nmero primo y es al menos el doble de N. Dependiendo del modo en que se resuelven las colisiones, existen varias modalidades de algoritmos Hash. En el mtodo Hash Abierto, cuando sucede colisin incrementamos el n H (H++) hasta que encontremos un puntero Hash vaci (valor NULL), en el cual insertaremos la direccin del dato a almacenar. En el mtodo Hash con Desbordamiento, cuando sucede colisin se almacenan las claves que producen colisin en otra matriz de punteros Hash llamada Matriz de Desbordamiento, en la que se almacenan las direcciones de dichas claves de forma consecutiva desde el primer elemento de la matriz. En el mtodo "Hash Enlazado", se aade a cada elemento de datos un nuevo campo puntero que sirva para apuntar mediante lista enlazada al siguiente elemento de datos que produjo colisin.

PROCEDIMIENTO DE BSQUEDA HASH: El procedimiento de bsqueda de datos ya almacenados (funcin HashOut()) consiste en obtener, a partir de la Clave del dato a buscar, el nmero entero H mediante la misma frmula H = Clave % L, y acudir a la posicin H de la matriz de punteros Hash para tomar la direccin de memoria del dato almacenado. Si en esa direccin no se encuentra la Clave buscada (hubo colisin al almacenarla), la bsqueda del dato correcto depende del mtodo Hash utilizado. Con el mtodo Hash Abierto incrementamos H y pasamos al siguiente puntero Hash, hasta que encontremos la Clave buscada o un puntero NULL, que significar que la clave no existe. Con el mtodo Hash con Desbordamiento pasaremos a explorar la matriz Hash de Desbordamiento secuencialmente desde su primer elemento, hasta que demos con la clave buscada o un puntero NULL. Con el mtodo "Hash Enlazado" seguimos la lista enlazada de datos a partir del dato actual, hasta dar con la clave buscada o lleguemos al final de la lista. PROCEDIMIENTO DE BORRADO HASH: Para eliminar un dato dado por su Clave, no basta con suprimir de la matriz Hash el puntero de posicin H=Clave%L, ponindolo a valor NULL. Con ello podramos perder tambin los dems datos de colisin asociados a esa misma posicin. Lo que se hace es aadir a cada elemento de datos un nuevo campo de tipo char llamado, por ejemplo, "Eliminado", campo que marcamos a valor "S" para indicar que este dato esta borrado. De esta forma no se pierde su Clave y los dems datos de colisin son alcanzables. Las funciones HashIn y HashOut debern tener en cuenta la existencia de este campo, para realizar bien su tarea.
EJEMPLO-03: /* Busqueda rpida mediante algoritmo Hash Abierto */ /* Almacenar DNI y Nombre de todos los alumnos de la Escuela EPS */ #include <stdio.h> #include <math.h> #include <stdlib.h> #define N 5000 //N mximo estimado de datos a almacenar (n de alumnos de la EPS) typedef struct { long dni; //dato clave de bsqueda, no repetible, identificativo de un alumno char nombre[50]; char eliminado; } tipoAlumno; //tipo de estructura contenedora de datos del alumno int numeroPrimo(int num); //calcula el primer n primo por encima del valor num int IniciarMatrizHash(void); //crea la matriz Hash de punteros, y la pone a cero int HashIn(tipoAlumno *p); //funcin de introduccin de un nuevo dato en la matriz Hash tipoAlumno *HashOut(long dni); //funcin de bsqueda de un dato en la matriz Hash void BorrarMatrizHash(void); //borrar todos los elementos creados mediante malloc //Variables globales: tipoAlumno **mHash = NULL; //puntero de control de la matriz Hash (es una matriz de punteros) int L; //n de elementos de la matriz Hash main() { tipoAlumno *p=NULL; //puntero auxiliar tipoAlumno alum, alumVacio={0, ""}; char resp; int i; L = numeroPrimo(2*N); //calculamos el n de elementos ms adecuado para la matriz Hash

if (!IniciarMatrizHash()) exit(1); //Crear la matriz Hash /* Introduccin de datos desde el teclado */ system("cls"); printf("N de elementos del array Hash: %d\n", L); printf("Introduzca datos de alumnos. Para terminar, DNI = 0\n"); while (1) { alum = alumVacio; //limpiar la variable alum printf("\nDNI: "); scanf("%ld", &alum.dni); fflush(stdin); //leer DNI if (alum.dni==0) break; //si DNI=0 salimos del bucle printf("Nombre: "); gets(alum.nombre); //leer Nombre p = (tipoAlumno *)malloc(sizeof(tipoAlumno)); //crear struct de tipo tipoAlumno if (p==NULL) printf("\nERROR: Memoria Insuficiente\n"); else { *p = alum; //copiar estructura alum en *p if (!HashIn(p)) free(p); //insertar elemento en la matriz Hash } } /* Impresion de los datos */ system("cls"); printf("Los datos introducidos son:\n\n"); for (i=0; i<L; i++) if (mHash[i]!=NULL && mHash[i]->eliminado!='S') printf("DNI: %ld\tNombre: %s\n", mHash[i]->dni, mHash[i]->nombre); /* Busqueda de datos usando la matriz Hash */ printf("\n\nBusqueda de datos. Para terminar, DNI = 0\n"); while (1) { printf("\nDNI: "); scanf("%ld", &alum.dni); fflush(stdin); //leer DNI if (alum.dni==0) break; //si DNI=0 salimos del bucle p = HashOut(alum.dni); //buscar DNI mediante la matriz Hash if (p==NULL) printf("Este DNI no existe\n"); else { printf("El nombre del alumno es: %s\n", p->nombre); do { printf("Eliminar este dato? (S/N): "); resp=toupper(getche()); } while (resp!='S' && resp!='N'); if (resp=='S') p->eliminado='S'; } } BorrarMatrizHash(); //borrar de memoria la matriz Hash y todos los datos de alumnos } int numeroPrimo(int num) //calcula el primer n primo por encima del valor num { int primo=0; //valor cierto o falso int i; int r=(int)sqrt((double)num); //lmite mximo del divisor if (num%2==0) num++; //si el n es par se pasa al impar siguiente while (!primo) { primo=1; for (i=3; i<=r && primo; i+=2) //bucle de divisores impares para num, hasta como mximo r if (num%i==0) primo=0; if (!primo) num+=2; //se pasa al n impar siguiente } return num; }

int IniciarMatrizHash(void) //crea la matriz Hash de punteros, y la pone a cero { int i=0; mHash = (tipoAlumno **)malloc(L * sizeof(tipoAlumno *)); //reservar espacio para el array Hash if (mHash==NULL) printf("\nERROR: Memoria Insuficiente\n"); else for (i=0; i<L; i++) mHash[i]=NULL; //poner a NULL todos los punteros return i; //retorna valor cierto o falso } int HashIn(tipoAlumno *p) //funcin de introduccin de un nuevo dato en la matriz Hash { int H; //indice de acceso del array Hash int conta=0; //contador de incrementos del indice int insertado=0; //valor cierto o falso H = p->dni % L; //clculo del indice de acceso al dato while (conta < L) //bucle de incrementos del indice { if (mHash[H]==NULL || mHash[H]->eliminado=='S') //elemento vaco, se inserta el dato { mHash[H]=p; insertado=1; break; } else if (mHash[H]->dni==p->dni) //clave repetida { printf("ERROR: clave duplicada\n"); insertado=0; break; } else //elemento no vaco, se incrementa el indice { conta++; H++; if (H==L) H=0; } } if (conta==L) //no ha habido ningun elemento vaco { printf("ERROR: la matriz esta llena\n"); insertado=0; } return insertado; //retorna cierto o falso } tipoAlumno *HashOut(long dni) //funcin de bsqueda de un dato en la matriz Hash { int H; //indice de acceso del array Hash int conta=0; //contador de incrementos del indice int encontrado=0; //valor cierto o falso H = dni % L; //clculo del indice de acceso al dato while (!encontrado && conta<L) { if (mHash[H]==NULL) return NULL; //si encontramos un elemento vaco if (mHash[H]->dni==dni && mHash[H]->eliminado!='S') //si encontramos el elemento buscado encontrado=1; else { conta++; H++; if (H==L) H=0; } //incrementamos el indice de acceso } if (conta==L) return NULL; //si la matriz esta llena y no hemos encontrado el dato buscado return mHash[H]; //retornamos la direccin del elemento encontrado } void BorrarMatrizHash(void) //borrar todos los elementos creados mediante malloc { int i; for (i=0; i<L; i++) free(mHash[i]); //borrar cada dato de los alumnos free(mHash); //borrar la matriz Hash de punteros }

Potrebbero piacerti anche