Punteros a funciones en C

Definición
Los punteros a funciones (pointer to functions, function pointers) proveen una increíble capacidad: la habilidad de llamar a una función desde otra. Este tipo de funciones comúnmente se conoce como callback.

Sintaxis
Antes de revisar su sintaxis, revisemos el prototipo de una función simple:

int sum(int, int);

La definición de sum es la siguiente:

int sum(int a, int b)
{
    return a + b;
}

Ahora bien, para definir un puntero a sum basta con agregar (* function_name) al nombre de la función. Por ejemplo, el siguiente fragmento define un puntero sum_ptr a sum:

int (*sum_ptr)(int, int) = sum;  // note que sum no use va seguido de parentesis

¿Qué significa todo esto? Usualmente se llamaría a la función sum de esta forma:

int suma = sum(10, 20);   // suma = 30

Pero ahora, es posible usar sum_ptr para llamar indirectamente a sum :

int suma = sum_ptr(10, 20);   // suma = 30

¿Redundante? Quizás. Aunque en este ejemplo parece complicar algo sencillo, la verdad es que utilizar punteros a funciones abre posibilidades a nuestros programas al permitir la ejecución de funciones diversas a partir de una misma.

Ejemplo 1. Ordenar un arreglo de enteros

Este ejemplo es clásico y probablemente el mejor para explicar el uso de callbacks. La función qsort es propia de c y permite ordenar un arreglo base. Lo particular de esta función es lo siguiente:

  • El arreglo base puede ser de cualquier tipo (int, char, etc)
  • Dado que base puede ser de cualquier tipo, hace falta definir una función de comparación para indicarle a qsort qué elemento es menor, mayor o igual.

qsort tiene el siguiente prototipo:

void qsort(void *base, 
           size_t nmemb, size_t size,
           int(*compar)(const void *, const void *));

en donde:

  • base Es un puntero al arreglo que se desea ordenar. Note que es de tipo
    void* lo cual significa que qsort no asume ningún tipo particular de arreglo.
  • nmemb Número de elementos del arreglo base.
  • size Tamaño (sizeof) de cada elemento en base. size_t es un alias de unsigned int.
  • compar Define el prototipo de la función callback para el ordenamiento.

Quizá no se ve a simple vista, pero compar recibe dos parámetros, p1 y p2, los cuales son punteros, y devuelve un entero:

int (*compar) (const void *p1, const void *p2))

Otro punto a considerar es que p1 y p2 son argumentos de compar, no de qsort.
Además, tienen el modificador const, lo cual significa que la función callback no debe modificar
a p1 ni p2, ya que este modificador previene su modificación.

El ejemplo completo es el siguiente:

#include <stdio.h>
#include <stdlib.h>

// prototipo de la funcion para
// comparar enteros
int int_cmp(const void *p1, const void *p2);

int main()
{
    // puntero a la function int_cmp
    int (*cmp)(const void *, const void *) = int_cmp;
    
    int arr[]  = {5, 4, 3, 2, 10, 1, 7};
    int nmemb = 7;
    int i;
    
    for (i=0; i<nmemb; i++)
        printf("%d ", arr[i]);


    // los tres casos son equivalentes
    // qsort(arr, nmemb, sizeof(int), int_cmp);       
    // qsort(arr, nmemb, sizeof(int), &int_cmp);
    qsort(arr, nmemb, sizeof(int), cmp);
    
    
    printf("\n");
    for (i=0; i<nmemb; i++)
        printf("%d ", arr[i]);
    
    return 0;
    
}

int int_cmp(const void *p1, const void *p2)
{
    int a = *(int *) p1;
    int b = *(int *) p2;
    
    if (a > b)
        return 1;
    
    if (b < a)
        return -1;
        
    else
        return 0;    
}

La salida del programa es la siguiente:

5 4 3 2 10 1 7 
1 2 3 4 5 7 10

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s