06. Apuntadores: accediendo a funciones I

Publicado por

De la misma forma en que podemos acceder a una variable o a una estructura sabiendo únicamente su dirección, así también nos podemos referir a una función. ¿Qué es lo que sucede en este último caso? Como sabemos, una función es una sección de código que, normalmente, desempeña una única tarea, por tanto, la dirección de una función hace referencia al principio de dicho bloque de instrucciones.

En esta primera parte, veremos cómo declarar, inicializar y usar un apuntador a función. La notación para crear este tipo de apuntadores puede parecer complicada en un principio pero, una vez que indaguemos en ella, verás que tiene todo el sentido del mundo.


Declaración de un apuntador a función

Para entender mejor la sintaxis de una declaración de un apuntador a función, recordemos primero cómo luce el prototipo de una función cualquiera:

<tipo_de_retorno> <identificador>([lista_de_parámetros]);

Como vemos, los prototipos de función carecen de cuerpo (la sección de código que realiza alguna tarea), por tanto, solo necesitamos conocer cuál es el nombre de la función, los parámetros que necesita para trabajar y el tipo de dato del valor que regresará al terminar de ejecutarse.

A este prototipo podemos convertirlo fácilmente en un apuntador a función, únicamente necesitamos el operador *, veamos cómo agregarlo usando el siguiente ejemplo:

// Prototipo.
void foo();

// Apuntador a función.
void (*foo)();

Sencillo, ¿no? Desde este momento, foo es el nombre de nuestro apuntador a función, estamos listos para recibir la dirección de una función cualquiera y ponernos a trabajar… o casi.

La sintaxis de dicho apuntador no es al azar, tiene un propósito; no podemos darle la dirección de cualquier función, solo podremos darle la dirección de alguna función que tenga las mismas características indicadas por el apuntador, en el caso del ejemplo, solo podrá hacerse cargo de funciones que no regresen nada y que no acepten ningún parámetro.

Así como tenemos apuntadores para variables de tipo char, de tipo int o de tipo float, así también tendremos que crear apuntadores para cada tipo de función, veamos algunos ejemplos:

// f1 aceptará funciones que regresen un "int" y acepten dos parámetros "int".
int (*f1)(int, int);

// f2 aceptará funciones que regresen un "double" y acepten un parámetro "float".
double (*f2)(float);

// f3 aceptará funciones que no regresen nada y acepten un parámetro de tipo apuntador a "char".
void (*f3)(char *);

Es muy importante no olvidar colocar los paréntesis para evitar que el compilador tenga un problema de interpretación. En el siguiente código se muestra qué entendería el compilador en cada caso:

int (*foo)(); // Un apuntador de nombre "foo" a una función sin parámetros y que regresa un valor entero.

int *foo(); // Una función de nombre "foo" sin parámetros y que regresa un valor de tipo "apuntador a entero".


Obtener la dirección de una función

Al igual que sucedía con los arreglos, podemos usar únicamente el identificador de una función si deseamos obtener su dirección. La forma de almacenar esta dirección es la misma que con los casos que hemos ido viendo a lo largo de la serie. El siguiente ejemplo nos muestra el procedimiento:

// Tenemos esta función.
int suma(int a, int b) {
  return a + b;
}

// Creamos un apuntador para ese tipo de función.
int (*funcionPtr)(int, int);

// Almacenamos la dirección de la función "suma" en nuestro apuntador.
funcionPtr = suma;

Es válido también usar el operador & para obtener la dirección pero, en realidad, esto es redundante y basta con solo poner el identificador.


¿Cómo utilizar un apuntador a función?

Hacer uso del apuntador es prácticamente igual que llamar a la función original, lo único que cambia es el identificador. Tomando de nuevo el apuntador de ejemplo que guarda la dirección de una función que suma dos números, la nueva forma de llamar a la función es la siguiente:

// Declaramos e inicializamos el apuntador.
int (*funcionPtr)(int, int) = suma;

// Usamos el apuntador.
printf("Resultado: %d", funcionPtr(2, 7));

// Lo anterior es lo mismo que usar la función original.
printf("Resultado: %d", suma(2, 7));

Si resulta lo mismo, ¿qué utilidad tiene el contar con estos apuntadores? Lo veremos más adelante.


Para terminar

Como había comentado al principio, lo más confuso en este tema era el asunto de la declaración, ya habiendo lidiado con ello estamos listos para saber qué podemos hacer con esta nueva herramienta y en qué partes nos es conveniente aplicarla. En la siguiente nota veremos algunos ejemplos.

Espero que lo básico de este tema te haya resultado de ayuda. Recuerda que cualquier duda, comentario o sugerencia de temas para este tipo de series son más que bienvenidos. Nos vemos en la próxima, hasta entonces.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *