0

Tutorial: punteros a funciones en C

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
}

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. 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 ID. 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;
}

Tr4nsduc7or

Originariamente creado cómo un galvanómetro de bolsillo, Transductor tomó consciencia de si mismo y fue despedido cuando en vez cumplir con su trabajo se dedicó a pensar teorías filosóficas sobre los hilos de cobre, los electrones y el Sentido del efecto Joule en el Universo. Guarda cierto recelo a sus creadores por no comprender la esencia metafísica de las metáforas de su obra. Actualmente trabaja a media jornada cómo antena de radio, y dedica su tiempo libre a la electrónica recreativa y a la filosofía.

Deja un comentario

avatar