Prac Apunt AFuncion Cucp PDF

Title Prac Apunt AFuncion Cucp
Course Algoritmo y estructura de datos
Institution Universidad Nacional Mayor de San Marcos
Pages 26
File Size 713.6 KB
File Type PDF
Total Downloads 8
Total Views 126

Summary

Teoría y practica...


Description

Temas interesantes C http://agora.pucp.edu.pe/inf2170681/contenidos.htm

PUNTEROS A FUNCIONES Hasta el momento, el estudio de los punteros se ha limitado a los datos de un programa; esto quiere decir que hemos definido variables capaces de almacenar direcciones de memoria en cuyas celdas se encuentran almacenados datos. Nos hemos acostumbrado a declarar variables de tipo puntero y asignarles direcciones válidas de manera dinámica (con el uso de new o malloc) o de manera estática (asignando la dirección de memoria de otra variable); sin embargo, la dirección que se le asigna a un puntero puede ser la de cualquier parte memoria, como se vio en el capítulo de punteros, y en esa dirección podemos encontrar información que se puede interpretar como un dato, al igual que como código ejecutable. Más aun, debemos recordar que un identificador es un nombre relacionado con una posición de memoria y estos sirven para denotar tanto las variables como las funciones, y por otro lado el identificador de una función está relacionado con la dirección de inicio del código de la función; por lo tanto, al igual que con las variables, el identificador de una función se puede emplear para asignar una dirección de memoria a un puntero. Pues bien, un puntero a función es puntero, con una forma de declaración especial, que puede recibir la dirección de inicio del código de una función y ejecutarla a través de él. La forma de declarar un puntero a función se muestra a continuación: (* identificador) (lista de parámetros); Por ejemplo, en la siguiente declaración: int (* operacion) (int, int); Define al identificador operacion como un puntero que es capaz de apuntar al inicio del código de cualquier función que tenga como argumentos a dos enteros y que devuelva un valor entero. Observe que la declatración anterior es muy distinta que la siguiente: int * operacion (int, int); En la primera, (*operación) se encuentra entre paréntesis y en la segunda no. Este detalle hace que, en el segundo caso, el compilador interprete a operacion como una función (ya no como un puntero) que recibe como parámetro dos enteros y que devuelve un puntero a un entero. Por lo tanto, debe tener presente que un puntero a función se debe definir encerrando entre paréntesis el asterisco y el identificador de manera obligatoria. Ej.

int (* operacion) (int, int); El siguiente ejemplo muestra de una manera muy sencilla:

#include float cubo(float x) { return x * x * x; } int main(void) { float (*ptrAFunc) (float); // Definimos el puntero a función float x = 3.5, xxx1, xxx2; xxx1 = cubo(x); printf("\nFuncion: Cubo de %f = %f\n", x, xxx1); ptrAFunc = cubo; // El puntero apunta al inicio del código de la función xxx2 = ptrAFunc(x); // Ejecutamos la función a través del puntero printf("\nPuntero: Cubo de %f = %f\n", x, xxx2); return 0; } Existen muchas formas de manejar un puntero a función, a continuación mostraremos alguas de éstas. En el primer ejemplo que presentamos veremos cómo emplear un puntero a funcón a través de un arreglo, y en el segundo se pretende pasar como parámetro a una función la dirección de inicio del código de otra función. Ejemplo 1: Uso de punteros a función a través de arreglos.

#include float suma(float x, float y) { return x + y; }

float resta(float x, float y) { return x - y; } float multiplica(float x, float y) { return x * y; } float divide(float x, float y) { return x / y; } int main(void) { // Se define un arreglo de punteros a funciones y se le asigna a cada // elemento la dirección de una función float (*operacion[4]) (float, float) = { suma, resta, multiplica, divide }; char oper[ ] = "+-x/"; float a = 45, b = 12, c; printf("Operaciones:\n"); for(int i = 0; i < 4; i++) { c = operacion[i] (a, b); // Ejecutamos la función a través de los // elementos del arreglo printf(" %6.2f %c %6.2f = %6.2f\n", a, oper[i], b, c); } return 0; } La respuesta de ese programa será como se muestra a continuación:

Ejemplo 2: Uso de punteros a función a como parámetros de otra función.

#include float suma(float x, float y) { return x + y; } float resta(float x, float y) { return x - y; } float multiplica(float x, float y) { return x * y; } float divide(float x, float y) { return x / y; } float fOpera(float x, float y, float (*f)(float, float)){ // El puntero f recibe la dirección de inicio de una función // no se sabe qué función será la recibida return f(x, y); // Ejecutamos la función a trav6eacute;s del puntero } int main(void) { float a = 45, b = 12, c; printf("Operaciones:\n"); // Se llama a la función fOpera pasando como par6aacute;metro la funció n suma c = fOpera(a, b, suma); printf(" Suma: %6.2f + %6.2f = %6.2f\n\n", a, b, c); // Se llama a la función fOpera pasando como par6aacute;metro la funció n multiplica c = fOpera(a, b, multiplica); printf(" Multiplica: %6.2f x %6.2f = %6.2f\n\n", a, b, c); // Se llama a la función fOpera pasando como par6aacute;metro la funció n resta c = fOpera(a, b, resta); printf(" Resta: %6.2f - %6.2f = %6.2f\n\n", a, b, c);

// Se llama a la función fOpera pasando como par6aacute;metro la funció n divide c = fOpera(a, b, divide); printf(" Divide: %6.2f / %6.2f = %6.2f\n\n", a, b, c); return 0; } Al ejecutar el programa se verá en la pantalla algo similar a lo que se muestra a continuación:

Funciones qsort y bsearch Las funciones qsort y bsearch son ejemplos de funciones que que reciben como parámetro un puntero a función. Estas funciones están definidas en la biblioteca de funciones stdlib.h. Se trata de funciones muy versátiles, en el caso de qsort, la función permite ordenar por le método quick sort, cualquier tipo de arreglo empleando cualquier criterio de ordenación, así por ejemplo se pueden ordenar un arreglos de cadenas de caractere, de valores enteros, de punto flotante, etc., tanto de manera ascendente como descendente. De manera similar la función bsearch permite encontrar un valor en cualquier tipo de arreglo empleando una búsqueda binaria. El lo que sigue mostraremos cómo trabaja la función qsort La sintaxis de la función qsort la presentamos a continuación: void qsort(void *datos, int numElem, int tamaño, int (*ptrFCmp)(const v oid *, const void *));

Se puede apreciar en la línea anterior que el parámetro datos es un puntero genérico (void *) por lo tanto puede cirecibir la dirección de memoria de cualquier tipo o estructura dedatos. Podría recibir la deirección de un arreglo de enteros (int *) para ordenar sus valores, también podría recibir la derección de un arreglo de punteros a char (char **) para poder ordenar cadenas de caracteres y de manera similar podría recibir la derección de memoria de un arreglo de punteros genéricoa (void **) para ordenar registros de datos, en fin cualquier cosa. El parámetro numElem debe recibir el número el número de elementos que tiene el arreglo datos. El parámetro tamaño debe recibir el tamaño en bytes del elemento del arreglo recibido. En otras palabras si datos es de tipo int *, entonces tamaño debe recibir sizeof(int), si es char ** debe recibir sizeof(char*), etc. El siguiente parámetro corresponde a un puntero a función, ptrFCmp es un puntero que puede recibir la dirección de cualquier función que reciba como argumentos dos direcciones de memoria que esten referidas a culaquier tipo de dato (void *) y que devuelva un entero. Si es cierto que el primer argumento permite trabajar con arreglos de cualquier tipo, este último argumento permite que la ordenación pueda realizarse empleando cualquier criterio. Esto se da porque en el código que emplea qsort para ordenar, a la hora de comparar dos elementos del arreglo para saber si están en el orden adecuado, la comparación se hace a través de la función asignada a ptrFCmp, por lo que dependerá de la forma en que el usuario implemente la función de comparación para establecer el criterio de ordenación. La función de comparación que se implemente para ser introducida como parámetro a la función qsort debe permitir recibir como parámerto las direcciones de dos elementos del arreglo (ver la figura siguiente), y devolver un valor entero que indique si es que el contenido del primer elemento del arrego recibido como parámetro es menor que el segundo (con un valor menor que cero), es igual que el segundo (valor igual a cero) o si es mayor (valor mayor que cero).

En el siguiente ejemplo podemos apreciar como se emplea la función qsort para ordenar una lista de nombres almacenados en un archivo de textos y una serie de valores numéricos almacenados en un arreglo. El archivo que contiene los nombres que se quiere ordenar, se presenta a continuación:

El código es el siguiente;

#include #include #include

// Función que compara dos cadenas de caracteres para ordenar alfabética mente int strComp(const void *v1, const void* v2) { char **s1 = (char**)v1, **s2 = (char**)v2; // Definimos s1 y s2 para no tener que trabajar con los punteros void return strcmp(*s1, *s2); // Para ordenar de mayor a menor // ==> return -strcmp(*s1, *s2); ó // ==> return strcmp(*s2, *s1); } // Función que compara dos valores enteros de menor a mayor int intComp(const void *v1, const void *v2) { int i1, i2; i1 = *(int*)v1; // Tomamos el valor apuntado por v1 y v2 i2 = *(int*)v2; return i1 - i2; // Para ordenar de mayor a menor ==> return i2 - i1; } // Prototipos de funciones de entrada y salida de cadenas de caracteres void leeCad(char **&, int &); void imprime(char **, int); int main(void) { // Ordenamos una lista de nombres en un arreglo de caracteres char **nombre; int numDat = 0, i; leeCad(nombre, numDat); qsort(nombre, numDat, sizeof(char *), strComp); imprime(nombre, numDat); // Ordenamos un arreglo con valores enteros int arr[] = {34, 56, 22, 36, 12, 7, 83, 18, 63, 14}; qsort(arr, 10, sizeof(int), intComp); printf("Los valores ordenados:\n"); for(i = 0; i < 10; i++)

printf("%4d", arr[i]); printf("\n"); return 0; } void leeCad(char **&nombre, int &numDat) { char *auxNomb[100], cad[100]; int tam, i; while (1) { gets(cad); tam = strlen(cad); if (tam == 0) break; auxNomb[numDat] = new char[tam+1]; strcpy(auxNomb[numDat], cad); numDat++; } nombre = new char*[numDat]; for(i = 0; i&lr; numDat; i++) nombre[i] = auxNomb[i]; } void imprime(char **nombre, int numDat) { int i; printf("Los nombres ordenados:\n"); for(i = 0; i 0 si i1 > i2 } int fechcmp(char *s1, char *s2) { // función para comparar las cadenas com o fechas char f1[11], f2[11]; s1[2] = s1[5] = s2[2] = s2[5] = 0; strcpy(f1, &s1[6]); strcpy(f2, &s2[6]); strcat(f1, "/"); strcat(f2, "/");

strcat(f1, &s1[3]); strcat(f2, &s2[3]); strcat(f1, "/"); strcat(f2, "/"); strcat(f1, s1); strcat(f2, s2); s1[2] = s1[5] = s2[2] = s2[5] = '/'; return strcmp(f1, f2); } void ordenar (char **datos, int izq, int der, int (*cmp)(char*, char*)){ int ultimo; if(izq >= der) return; intercambiar(datos, izq, (izq + der)/2); ultimo = izq; for (int i = izq + 1; i 0) break; // Compara dosVdatos, devuelve 0 si // son iguales, un valor mayor que cereo si el primero es mayor que // el segundo y un valor menor que cereo si el primero es menor que // el segundo. El tipo de dato que comprar depende de la funció na // la que apunte el puntero ant = p; p = (void**)(p[1]); } nuevo[1] = p; if(ant == NULL) lista = nuevo; else ant[1] = nuevo; } void imprimeLista(void *lista, void (*impDato)(void *)) { void **lst;

lst = (void**)lista; while (lst) { impDato(lst[0]); // imprime un dato sea cualfuera su naturaleza lst = (void**)lst[1]; } } void eliminaLista(void *lista, void (*elimDato)(void *)) { void **lst, **sale; lst = (void**)lista; while (lst) { elimDato(lst[0]); // elimina un dato sea cual fuera su naturaleza sale = lst; lst = (void**)lst[1]; delete [ ]sale; } } 3 - Biblioteca de funciones ManipDato.h y ManipDatos.cpp Esta biblioteca es la que manipulará los datos que se desea colgar de la lista simplemente ligada. Se deberá implementar las funciones de ella cada vez que se quiera colgar un tipo de dato diferente. Los prototipos de las funciones no deben cambiar y debe ser considerados como una plantilla. A continuación se presenta el archivo Manip.h: //**************************************************** // // Módulo: ManipDato.h // // Funciones que permiten crear, imprimir, // eliminar y comparar un dato específico según // una plantilla dada // //**************************************************** #ifndef _MANIPDATO_H

#define _MANIPDATO_H void * leeDato(void); // Lee un dato a través de un putero, devuelve un puntero genérico // al dato o NULL en caso que se hayan terminado los datos. int compDato(void *, void *); // Compara dos datos a través de dos punteros void. Si son iguales // devuelve cero, si el primeto es menor que el segundo, se devuelve // un valor menor que cero y encaso contrario un valor mayor que cero. void impDato(void *); // Imprime un dato a través de un puntero void. void elimDato(void *); // Elimina un dato a través de un puntero void. #endif /* _MANIPDATO_H */ A partir de estos prototipos se podrá implementar las funciones de acuerdo al tipo de datos que se quiere procesar. Veamos algunos casos: a) - Manipulación de datos enteros En este caso se quiere colgar vaolres numéricos enteros en la lista, entonces el código de las funciones será como sigue: //**************************************************** // // Módulo: ManipDato.cpp Versión 1 // // Módulo de implementación de la funciones // para manejar valores enteros // //**************************************************** #include void * leeDato(void) { // lee un entero, si se intenta leer el fin del archivo // devuelve NULL int dato, *ptDato; if (scanf("%d", &dato) == EOF) return NULL;

ptDato = new int; *ptDato = dato; return ptDato; } int compDato(void *v1, void *v2) { int *n1, *n2; // Compara los valores enteros apuntados por los punteros n1 = (int *)v1; n2 = (int *)v2; return *n1 - *n2; } void impDato(void *v) { int *n; // Imprime los valores enteros apuntados por los punteros n = (int *)v; printf("%d\n", *n); } void elimDato(void *v) { int *n; // Elimina los valores enteros apuntados por los punteros n = (int *)v; delete n; } b) - Manipulación de cadenas de caracteres //**************************************************** // // Módulo: ManipDato.cpp Versión 2 // // Módulo de implementación de la funciones // para manejar cadenas de caracteres // //**************************************************** #include #include

void * leeDato(void) { // lee una cadena, si se intenta leer una cadena // vacía develve NULL char *dato, aux[300]; int tam; if (gets(aux) == NULL) return NULL; tam = strlen(aux); if (tam == 0) return NULL; dato = new char[tam + 1]; strcpy(dato, aux); return dato; } int compDato(void *v1, void *v2) { char *n1, *n2; // Compara las cadenas de caracteres apuntadas por los punteros n1 = (char *)v1; n2 = (char *)v2; return strcmp(n1, n2); } void impDato(void *v) { char *n; // Imprime las cadenas apuntados por los punteros n = (charv*)v; printf("%s\n", n); } void elimDato(void *v) { char *n; // Elimina las cadenas apuntadas por los punteros n = (char *)v; delete [ ]n; } c) - Manipulación de registros de datos

//**************************************************** // // Módulo: ManipDato.cpp Versión 3 // // Módulo de implementación de la funciones // para manejar registros de datos // //**************************************************** #include #include void * leeDato(void) { void **dato; char auxStr[300], *nombre, *fechIng; //...


Similar Free PDFs