70
1 DIAPOSITIVAS DE "PROGRAMACIÓN" GRADO EN INGENIERÍA EN TECNOLOGÍAS DE LA TELECOMUNICACIÓN GRADO EN INGENIERÍA EN SISTEMAS DE TELECOMUNICACIÓN GRADO EN INGENIERÍA ELECTRÓNICA DE COMUNICACIONES GRADO EN INGENIERÍA TELEMÁTICA Escuela Politécnica Superior Universidad de Alcalá

DiapositivasTeoria

Embed Size (px)

Citation preview

  • 1

    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

  • 2

    NDICE

    SEMANA 1.1. REPASO DE PUNTEROS

    CONCEPTOS FUNDAMENTALES

    ERRORES TPICOS EN EL USO DE PUNTEROS

    OPERACIONES CON PUNTEROS

    PRIORIDADES CON LOS OPERADORES ++ Y --

    SEMANA 1.2. REPASO DE PUNTEROS (II)

    PUNTEROS GENRICOS

    PUNTEROS Y ARRAYS DE DATOS

    PUNTEROS A CADENAS DE CARACTERES

    ARRAYS DE PUNTEROS

    PUNTEROS A PUNTEROS

    PUNTEROS A ESTRUCTURAS Y UNIONES

    SEMANA 2.1. REPASO DE PUNTEROS (III)

    ASIGNACIN DINMICA DE MEMORIA

    PASO DE ARGUMENTOS POR REFERENCIA

    SEMANA 2.2. MS SOBRE PUNTEROS

    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

    SEMANA 3. 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

    SEMANA 4. RECURSIVIDAD, ARGUMENTOS EN LNEA DE

    RDENES

    FUNCIONES RECURSIVAS

    ARGUMENTOS EN LNEA DE RDENES

  • 3

    SEMANA 5. 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

    SEMANA 6. 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

    SEMANA 7. MS SOBRE FICHEROS

    ELIMINACION DE FICHEROS

    CAMBIAR EL NOMBRE DE FICHEROS

    COPIAR FICHEROS

    ESCRIBIR DATOS EN LA IMPRESORA

    VER DIRECTORIOS DE FICHEROS

    FICHEROS TEMPORALES

    SEMANA 8. TIPOS DE ACCESO A FICHEROS

    ACCESO SECUENCIAL A FICHEROS

    ACCESO ALEATORIO A FICHEROS

    SEMANA 9. APLICACIN PRCTICA DE FICHEROS

    CREACIN DE UNA BASE DE DATOS SENCILLA

    SEMANA 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

  • 4

    BSQUEDA DE ELEMENTOS EN LA LISTA

    ORDENACIN DE LOS ELEMENTOS DE LA LISTA

    SEMANA 11. ESTRUCTURAS DINMICAS (II)

    PILAS

    COLAS

    LISTAS CIRCULARES

    LISTAS DOBLEMENTE ENLAZADAS

    SEMANA 12. RBOLES BINARIOS

    CONCEPTOS GENERALES

    RBOLES BINARIOS

    RBOLES BINARIOS DE BSQUEDA

    RECORRIDO DE RBOLES BINARIOS

    BSQUEDA EN RBOLES BINARIOS DE BSQUEDA

    SEMANA 13. LGORITMOS DE ORDENACIN DE DATOS

    CONCEPTOS GENERALES

    MTODO DE LA BURBUJA

    MTODO DE SELECCIN

    MTODO DE INSERCIN

    MTODO RECURSIVO QUICKSORT

    COMPARATIVA DE ALGORITMOS

    SEMANA 14. LGORITMOS DE BSQUEDA DE DATOS

    BSQUEDA SECUENCIAL

    BSQUEDA BINARIA

    ALGORITMOS HASH

    PROCEDIMIENTO DE ALMACENAMIENTO HASH

    PROCEDIMIENTO DE BSQUEDA HASH

    PROCEDIMIENTO DE BORRADO HASH

  • 5

    1.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]

  • 6

    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 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;

  • 7

    1.2. REPASO DE PUNTEROS (II)

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

  • 8

    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-01. // PUNTEROS A CADENAS #include 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 ha 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 }

    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-02. // ARRAY DE PUNTEROS #include 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

  • 9

    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-03. // PUNTEROS A ESTRUCTURAS #include 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 }

  • 10

    2.1. REPASO DE PUNTEROS (III)

    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

    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-01. // ASIGNACION DINMICA DE MEMORIA #include #include 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-02. // ARRAY DINAMICO #include #include

  • 11

    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

  • 12

    } else { p=paux; //actualizar puntero maestro p p[num-1]=dato; //copiar dato al array dinmico } } printf("\nLos datos introducidos son:\n"); for (i=0; i

  • 13

    2.2. MS SOBRE PUNTEROS

    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 ambos caso, la variable receptora

    acta como un puntero variable que puede ser modificado.

    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-01. // PASO DE ARRAYS UNIDIMENSIONALES COMO ARGUMENTOS A FUNCIONES // Programa para copiar un array en otro #include 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

  • 14

    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-02. // PASO DE ARRAYS BIDIMENSIONALES COMO ARGUMENTOS A FUNCIONES // Programa para copiar un array bidimensional en otro #include #define FILAS 3 #define COLS 2 void CopiaArray1(int x[][COLS], int y[][COLS]); void CopiaArray2(int *x, int *y); void CopiaArray3(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); //las tres funciones hacen lo mismo CopiaArray2((int *)a, (int *)b); //de maneras diferentes CopiaArray3((int *)a, (int *)b); printf("Los datos copiados en el array b son: "); for (i=0; i

  • 15

    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-03. // PASO DE PUNTEROS COMO ARGUMENTOS A FUNCIONES // Programa para encontrar el dato mayor de un array #include 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; ix[posMayor]) posMayor = i; return x+posMayor; //retorna la direccin del elemento mayor del array } void buscaMayor2(int *x, int numelem, int **pp) //recibe el puntero p por referencia { int i, posMayor=0; for (i=1; ix[posMayor]) posMayor = i; *pp = x+posMayor; //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-04. // FUNCIN QUE RETORNA UNA DIRECCIN

  • 16

    // Funcin para devolver un mensaje habitual dependiendo de un cdigo numrico #include 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

  • 17

    3. 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 #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

  • 18

    } 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->mesmes>12 || scn!=1); if (tf->mes==0) return; do { printf("Ao: "); scn=scanf("%d",&tf->anyo); fflush(stdin); } while (tf->anyoeuros); fflush(stdin); } while (tf->euros

  • 19

    EJEMPLO-02. // PUNTEROS A FUNCIONES // Men de operaciones matemticas #include #include #include double cuadrado(double n1); //prototipos de las funciones auxiliares double logaritmo(double n1); double raiz(double n1); int menu(void); 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) //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. Salir.\n"); printf("\nSeleccione la operacin a realizar: "); scanf("%d", &op); //pedir por teclado la opcin deseada } while (op3); return (op-1); }

  • 20

    4. 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 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 (numero25); //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); }

  • 21

    EJEMPLO-02: // CUENTA ASCENDENTE Y DESCENDENTE, RECURSIVA E ITERATIVA #include void ascend(int); void descend(int); main() { int i, numero=0; do { printf("Dame numero positivo:"); scanf("%d", &numero); } while (numero0) 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 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)

  • 22

    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).

    Tras el ltimo puntero argv[n] se incluye un puntero final de valor NULL.

    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() (para convertir a un valor entero) o atof() (para convertir a un valor real con

    decimales).

    En la lnea de comandos, los distintos argumentos de entrada deben ir separados por un espacio en blanco. Si no hay espacios se considera un nico argumento.

    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 Indica que el operando elegido es el XXX #include

  • 23

    #include #include double cuadrado(double n1); //prototipos de las funciones auxiliares double logaritmo(double n1); double raiz(double n1); int menu(void); 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; i0 && op

  • 24

    5. 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

  • 25

    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.

  • 26

    Si no se cierra un fichero con fclose, se cerrar automticamente al terminar la ejecucin del programa.

    EJEMPLO-02: // FUNCIONES FOPEN Y FCLOSE #include 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 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))

  • 27

    { 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 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); }

    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.

  • 28

    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-05: // FUNCIONES PARA POSICIONAMIENTO DEL APUNTADOR DE L/E #include 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.

  • 29

    6. 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 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

  • 30

    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 #include #include #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

  • 31

    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 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);

  • 32

    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);

  • 33

    EJEMPLO-04: // FUNCIONES FWRITE Y FREAD // Programa para cargar desde teclado datos de consumo telefnico mensual #include #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; imes==0) return; do { printf("Ao: "); scn=scanf("%d",&tf->anyo); fflush(stdin); } while (tf->anyoeuros); fflush(stdin); } while (tf->euros

  • 34

    7. MS SOBRE FICHEROS

    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");

    EJEMPLO-01. // BORRAR, RENOMBRAR Y COPIAR FICHEROS #include 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")) { ...... }

  • 35

    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); }

  • 36

    EJEMPLO-02: // USO DE LA IMPRESORA // Impresin de un fichero de texto #include 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 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); }

  • 37

    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 }

    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");

  • 38

    8. TIPOS DE ACCESO A FICHEROS

    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-01: // ACCESO SECUENCIAL A REGISTROS // Programa para cargar desde teclado datos de consumo telefnico mensual #include #include #include struct datotfno //declaracin del tipo estructura { int mes,anyo; float euros; }; int introducir(struct datotfno *tf); void imprimir(FILE *pf); char sino(char *mensaje); 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);

  • 39

    } 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->mesmes>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->anyoeuros); fflush(stdin); } while (tf->euros=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"); //desactivar indicadores de error } 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 }

    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.

  • 40

    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:

    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.

    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.

    EJEMPLO-02: // ACCESO ALEATORIO A REGISTROS // Programa para cargar desde teclado datos de consumo telefnico mensual #include #include #include void verregistro(void); //Prototipos de funciones auxiliares void nuevoreg(void); void modireg(void); int introducir(void); void primero(void); void ultimo(void); void avanzar(void); void iranumero(void); struct datotfno //declaracin del tipo estructura (registro) { int mes,anyo; float euros; }; //Variables globales: int numregs=0; //N total de registros del fichero int regnum=0; //N del registrro actual int tamreg=sizeof(struct datotfno); //Tamao en bytes de un registro struct datotfno dato; //Variable tipo registro para toma de datos FILE *pf; main() { int menu=0, salir=0; 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 = (int)ftell(pf)/tamreg; //obtenemos el n de registros del fichero

  • 41

    rewind(pf); //Nos posicionamos en el primer registro } if (numregs==0) nuevoreg(); while (!salir) { system("cls"); verregistro(); printf("MENU DE OPCIONES:\n\n"); printf("1. Introducir Nuevos Registros\n"); printf("2. Modificar Registro Actual\n"); printf("3. Ir al Primer Registro\n"); printf("4. Ir al Ultimo Registro\n"); printf("5. Avanzar Registros\n"); printf("6. Ir a Registro N\n"); printf("0. Salir\n"); do { printf("\nElija opcin: "); scanf("%d",&menu); fflush(stdin); } while (menu6); switch (menu) { case 1: nuevoreg(); break; case 2: modireg(); break; case 3: primero(); break; case 4: ultimo(); break; case 5: avanzar(); break; case 6: iranumero(); break; case 0: salir=1; break; } } fclose(pf); } void verregistro(void) { fread(&dato, tamreg, 1, pf); //Leer registro actual fseek(pf,-tamreg,SEEK_CUR); //Volver a la posicin inicial printf("Hay %d registros en el fichero.\n",numregs); printf("N registro actual: %d\n",regnum); printf("Datos del registro actual:\n"); printf(" N Mes: %d Ao: %d Euros Tfno: %g\n\n", dato.mes, dato.anyo, dato.euros); } void nuevoreg(void) { int n; fseek(pf,0,SEEK_END); //Apuntador L/E al final del fichero system("cls"); printf("Introduzca los datos de consumo telefnico:\n"); do //bucle para introduccin de registros de forma secuencial { printf("\nRegistro N %d:\n",numregs); if (n=introducir()) //instruccin de asignacin, no de comparacin { fwrite(&dato, tamreg, 1, pf); numregs++; } //grabar registro e incrementar numregs } while (n); //continua mientras n!=0 rewind(pf); regnum=0; //Apuntador L/E al primer registro del fichero } void modireg(void) {

  • 42

    system("cls"); printf("Introduzca los datos de consumo telefnico:\n"); printf("\nRegistro N %d:\n",regnum); if (introducir()) { fwrite(&dato, tamreg, 1, pf); //grabar registro fseek(pf,-tamreg,SEEK_CUR); //volver a su posicin de inicio } } int introducir(void) //funcin para introduccin de datos por teclado { int scn; do { printf("N de Mes (0=Terminar): "); scn=scanf("%d",&dato.mes); fflush(stdin); } while (dato.mes12 || scn!=1); if (dato.mes==0) return 0; //retorna 0 para terminar do { printf("Ao: "); scn=scanf("%d",&dato.anyo); fflush(stdin); } while (dato.anyo

  • 43

    9. APLICACIN PRCTICA DE FICHEROS

    CREACIN DE UNA BASE DE DATOS SENCILLA:

    Vamos a 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 #include 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)

  • 44

    { 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 (m8); 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");

  • 45

    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 }

  • 46

    } } 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 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)

  • 47

    { //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

  • 48

    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 util