(Última revisión 22/09/2020)
Saludos, humano. Como sabrás, cuándo programamos en C disponemos de punteros a varios tipos de datos (char*, int*, float*, double*) que nos permiten hacer muchas cosas: reservar memoria dinámica, crear referencias y alias, etc. Lo que quizá no sabías es que también podemos crear punteros que apunten a funciones como si fueran cualquier otro tipo de datos.
En los ejemplos de este tutorial verás cómo declarar punteros a funciones y como utilizarlos de varias formas distintas.
1. Puntero a función
He aquí un ejemplo básico. Tenemos una función que escribe un texto por pantalla, y la llamamos a través de un puntero:
#include <stdio.h> //Funcion de ejemplo void funcion() { printf("Se ha entrado en la funcion\n"); } int main() { //Creamos el puntero a la funcion void (*puntero_funcion)() = &funcion; //Llamamos la funcion a traves del puntero puntero_funcion(); return 0; }
Como puedes ver, no tiene demasiada diferencia con el resto de punteros. Vamos a complicarlo un poco más…
2. Función con parámetros
Si la función a la que quieres apuntar recibe algún parámetro, hay que indicar sus tipos al declarar el puntero. Fíjate en este ejemplo: la función recibe dos enteros y muestra sus valores. Nótese que cuándo se declara el puntero se escriben los tipos de los argumentos entre paréntesis.
#include <stdio.h> //Funcion que recibe dos enteros void funcion(int valor1, int valor2) { printf("Valor 1 = %d\n", valor1); printf("Valor 2 = %d\n", valor2); } int main() { //Se crea el puntero a la funcion. Hay que indicar el tipo de los parametros que recibe. void (*puntero_funcion)(int, int) = &funcion; //Se llama la funcion a traves del puntero puntero_funcion(2, 5); return 0; }
3. Función como parámetro a otra función
Las funciones también pueden pasarse como parámetros a otras funciones. En este código se define una función principal que recibe como parámetros dos números a, b y un puntero a una función entera. La función principal pasa a y b como parámetros a la función del puntero y escribe el valor que devuelve por pantalla.
#include <stdio.h> int sumar(int a, int b) { return a+b; } int restar(int a, int b) { return a-b; } void funcion_principal(int a, int b, int (*funcion)(int, int)) { int resultado = funcion(a,b); printf("El resultado es %d\n", resultado); } int main() { //Se definen dos valores enteros cualesquiera int num1 = 5; int num2 = 4; //Se invoca la funcion principal, pasandole la funcion de SUMA printf("\nSuma:\n"); funcion_principal(num1, num2, sumar); //Se invoca la funcion principal, pasandole la funcion de RESTA printf("Resta:\n"); funcion_principal(num1, num2, restar); return 0; }
Con este ejemplo puede verse que los punteros a funciones sirven para eliminar la redundancia en el código: al pasar la función como parámetro nos ahorramos tener que escribir variantes de la función principal para realizar cada una de las operaciones.
4. Función qsort()
Un ejemplo práctico del caso anterior es la función qsort(). Esta función propia de C permite ordenar cualquier vector utilizando el algoritmo Quick Sort. La signatura de la función qsort() en C es:
void qsort (void* array, size_t nelementos, size_t bytes, int (*comparador)(const void*,const void*));
dónde void* array es un puntero al vector que se quiere ordenar, nelementos es el número de elementos dentro del vector y bytes es el tamaño que ocupan.
El parámetro más importante es el último: el puntero a la función comparador. Para ordenar un vector hace falta un criterio para comparar dos elementos y decidir cuál de ellos va primero. Por ejemplo, en el caso de que los elementos del vector sean enteros, basta con comparar sus valores. Pero ordenar alfabéticamente un vector de Strings (u otro tipo de estructura más compleja) puede requerir de funciones más elaboradas.
La función qsort() de C está pensada para poder ordenar un vector de cualquier tipo, y para ello el programador debe pasarle como parámetro la función para comparar elementos. Puedes programar esta función comparadora a tu gusto, pero debe recibir como argumentos dos punteros void (correspondientes a dos elementos del vector) y devolver 1 o -1 según cuál de los dos elementos vaya primero.
int comparador(const void * a, const void * b) { //Tu codigo aqui dentro. Tiene que comparar a,b y devolver -1 ó 1. }
Esta función tendrás que pasarla como parámetro a qsort().
Admito que esto puede parecer complicado a primera vista, pero en realidad es muy sencillo. Veamos un ejemplo: supón que tenemos un struct correspondiente a la información de un envase de un cierto producto. Este struct tiene definidos tres campos: un identificador, la capacidad (en mililitros) del envase y su precio. Si quisiéramos ordenar un vector de envases con la función qsort(), habría que definir una función para comparar dos envases. En este ejemplo esto se hace de dos formas distintas: por identificador y según el precio por unidad de volumen.
#include <stdio.h> struct Envase { int id; //Identificador int volumen; //Volumen en mililitros float precio; //Precio en euros }; /* Metodo de ordenacion I: ordenar por identificador*/ int ordenarID (const void * a, const void * b) { /*Se hace un casting para convertir los punteros void a punteros de Envases y se guarda su identificador*/ int id_a = ((struct Envase*)a)->id; int id_b = ((struct Envase*)b)->id; //Se comparan los dos identificadores if(id_a > id_b) return 1; else return -1; } /* Metodo de ordenacion II: ordenar por precio/unidad de volumen */ int ordenar_precio_volumen (const void * a, const void * b) { /*Se hace un casting para convertir los punteros void a punteros de Envases y se guarda su volumen y precio*/ float precio_a = ((struct Envase*)a)->precio; int volumen_a = ((struct Envase*)a)->volumen; float precio_b = ((struct Envase*)b)->precio; int volumen_b = ((struct Envase*)b)->volumen; //Se calcula precio/volumen float val_a = precio_a/volumen_a; float val_b = precio_b/volumen_b; //Se comparan los dos valores if(val_a > val_b) return 1; else return -1; } int main() { int dimension = 4; //Dimension del vector struct Envase array[4]; //Vector de envases array[0].id = 5; array[0].volumen = 600; array[0].precio = 12; array[1].id = 6; array[1].volumen = 550; array[1].precio = 7.99; array[2].id = 1; array[2].volumen = 400; array[2].precio = 25; array[3].id = 3; array[3].volumen = 600; array[3].precio = 9.5; //Ordenar el vector por ID qsort((void*)array, dimension, sizeof(struct Envase), ordenarID); //Mostrar el vector por ID. El output es {1 3 5 6} printf("\nOrdenado por ID: {"); for(int i = 0; i < dimension; i++) { printf("%d ", array[i].id); } printf("}\n"); //Ordenar el vector por precio/volumen qsort((void*)array, dimension, sizeof(struct Envase), ordenar_precio_volumen); //Mostrar el vector por precio/volumen. El output ahora es {6 3 5 1} printf("\nOrdenado por precio/vol: {"); for(int i = 0; i < dimension; i++) { printf("%d ", array[i].id); } printf("}\n\n"); return 0; }
Como es lógico, puedes hacer funciones comparadoras tan complicadas como quieras. Este es uno de los puntos fuertes del lenguaje C, su enorme flexibilidad.
5. Funciones como Arrays
C también permite declarar arrays de punteros a funciones. Este código define tres funciones para hacer comprobaciones sobre un número (si es par, si es positivo y si es primo) y las realiza sobre un entero introducido por el usuario. La gracia es que en vez de llamar las tres funciones individualmente, se guardan en un vector y éste se recorre con un bucle for.
#include <stdio.h> /*Comprobar si el numero es PAR*/ void funcion1(int a) { if(a % 2 == 0) printf("El numero es par\n"); else printf("El numero es impar\n"); } /*Comprobar si el numero es POSITIVO/NEGATIVO*/ void funcion2(int a) { if(a > 0) printf("El numero es positivo\n"); else if(a < 0) printf("El numero es negativo\n"); else printf("El numero es cero\n"); } /*Comprobar si el numero es PRIMO*/ void funcion3(int a) { if(a <= 1) { printf("El numero NO es primo\n"); return; } for(int i = 2; i <= (a)/2; i++) { if ( a%i == 0 ) { printf("El numero NO es primo\n"); return; } } printf("El numero es primo\n"); } int main() { //Vector de funciones void (*array[]) (int) = {funcion1, funcion2, funcion3}; //Se pide al usuario que entre un numero int val; printf("\nIntroduce un numero entero: "); scanf("%d", &val); //Se recorre el vector para llamar las funciones for(int i = 0; i < 3; i++) { array[i] (val); } return 0; }
hola, sobre el array de funciones lo he implementado alguna vez en el tipico menu en que se elige la poción 1..4 pongo el número en el array y listo 😉
queria hacer una consulta abierta, es posible declarar funciones dentro de un struct ??
esto haria que funcionase parecido a un metodo de una clase de c++, ej alumno[cnt].imprimir();
gracias por su tiempo
Saludos vicvic.
Por lo que sé, no es posible crear directamente una función dentro de un struct como si se tratase una clase. No obstante, aplicando lo que has aprendido en este tutorial, dentro del struct podrías crear un puntero a una función externa.
Gracias por el atrículo, he entendido perfectamente de lo ue se trata y he visto fácilmente cómo usarlo en mi aplicación
Excelente artículo!
Muchas gracias, muy interesante y útil