Variables de Puntero a Función
Declaración de Punteros a Función
Los punteros a función son variables que almacenan direccciones de funciones, permitiendo invocar dichas funciones a través del puntero. Veamos un ejemplo básico:
#include <stdio.h>
void saludar() {
printf("Hola mundo\n");
}
int main() {
printf("Dirección de saludar: %p\n", saludar);
printf("Dirección con &: %p\n", &saludar);
return 0;
}
Como se observa, las funciones tienen dirección de memoria, y podemos almacenarla en punteros especializados:
void funcion_ejemplo() {
printf("Ejecutando función\n");
}
int sumar(int a, int b) {
return a + b;
}
// Declaración de punteros a función
void (*ptr_funcion)() = &funcion_ejemplo;
void (*ptr_funcion2)() = funcion_ejemplo;
int (*ptr_sumar)(int, int) = sumar;
Utilización de Punteros a Función
Una vez declarado el puntero, podemos utilizarlo para invocar la función referencaida:
#include <stdio.h>
int multiplicar(int x, int y) {
return x * y;
}
int main() {
int (*operacion)(int, int) = multiplicar;
printf("Resultado: %d\n", (*operacion)(5, 3));
printf("Resultado: %d\n", operacion(4, 7));
return 0;
}
Redefinición de Tipos con typedef
La palabra clave typedef permite crear alias para tipos complejos, facilitando su uso:
typedef unsigned int uint;
typedef int* ptr_entero;
// Para punteros a función
typedef int(*ptr_operacion)(int, int);
typedef void(*ptr_procedimiento)();
Arreglos de Punteros a Función
Un arreglo de punteros a función permite almacenar múltiples direcciones de funciones del mismo tipo:
int (*operaciones[4])(int, int);
Tablas de Despacho
Las tablas de despacho utilizan arreglos de punteros a función para simplificar código cnodicional complejo. Consideremos una calculadora:
#include <stdio.h>
int suma(int a, int b) { return a + b; }
int resta(int a, int b) { return a - b; }
int producto(int a, int b) { return a * b; }
int division(int a, int b) { return a / b; }
int main() {
int x, y, opcion = 1;
int resultado;
// Tabla de despacho
int (*calculos[5])(int, int) = { NULL, suma, resta, producto, division };
do {
printf("Calculadora\n");
printf("1. Suma 2. Resta\n");
printf("3. Producto 4. División\n");
printf("0. Salir\n");
printf("Seleccione: ");
scanf("%d", &opcion);
if (opcion >= 1 && opcion <= 4) {
printf("Ingrese dos números: ");
scanf("%d %d", &x, &y);
resultado = calculos[opcion](x, y);
printf("Resultado: %d\n", resultado);
} else if (opcion == 0) {
printf("Finalizando...\n");
} else {
printf("Opción inválida\n");
}
} while (opcion != 0);
return 0;
}
Funciones de Retrollamada
Concepto de Retrollamada
Una función de retrollamada se invoca indirectamente a través de un puntero pasado como argumento:
#include <stdio.h>
int suma(int a, int b) { return a + b; }
int resta(int a, int b) { return a - b; }
void ejecutar_operacion(int (*operacion)(int, int)) {
int x, y, resultado;
printf("Ingrese dos valores: ");
scanf("%d %d", &x, &y);
resultado = operacion(x, y);
printf("Resultado: %d\n", resultado);
}
int main() {
int opcion;
do {
printf("1. Suma 2. Resta 0. Salir\n");
printf("Seleccione: ");
scanf("%d", &opcion);
switch(opcion) {
case 1: ejecutar_operacion(suma); break;
case 2: ejecutar_operacion(resta); break;
case 0: printf("Saliendo\n"); break;
default: printf("Opción no válida\n");
}
} while (opcion != 0);
return 0;
}
Implementación de qsort Personalizado
La función qsort de la biblioteca estándar utiliza retrollamadas para ordenamiento genérico:
#include <stdio.h>
#include <string.h>
struct Persona {
char nombre[30];
int edad;
};
int comparar_edad(const void* a, const void* b) {
return ((struct Persona*)a)->edad - ((struct Persona*)b)->edad;
}
int comparar_nombre(const void* a, const void* b) {
return strcmp(((struct Persona*)a)->nombre, ((struct Persona*)b)->nombre);
}
void intercambiar(void* a, void* b, size_t tam) {
unsigned char* ptr_a = (unsigned char*)a;
unsigned char* ptr_b = (unsigned char*)b;
unsigned char temp;
for (size_t i = 0; i < tam; i++) {
temp = ptr_a[i];
ptr_a[i] = ptr_b[i];
ptr_b[i] = temp;
}
}
void ordenar_burbuja(void* base, size_t num, size_t tam,
int (*comparar)(const void*, const void*)) {
for (size_t i = 0; i < num - 1; i++) {
for (size_t j = 0; j < num - i - 1; j++) {
void* actual = (char*)base + j * tam;
void* siguiente = (char*)base + (j + 1) * tam;
if (comparar(actual, siguiente) > 0) {
intercambiar(actual, siguiente, tam);
}
}
}
}
int main() {
struct Persona personas[] = {
{"Carlos", 25},
{"Ana", 30},
{"Luis", 20}
};
size_t cantidad = sizeof(personas) / sizeof(personas[0]);
ordenar_burbuja(personas, cantidad, sizeof(struct Persona), comparar_edad);
for (size_t i = 0; i < cantidad; i++) {
printf("%s: %d años\n", personas[i].nombre, personas[i].edad);
}
return 0;
}