Punteros a funciones

Tanto en C como en C++ se pueden declarar punteros a funciones.

Sintaxis:

<tipo> (*<identificador>)(<lista_de_parámetros>);

De esta forma se declara un puntero a una función que devuelve un valor de tipo <tipo> y acepta la lista de parámetros especificada. Es muy importante usar los paréntesis para agrupar el asterisco con el identificador, ya que de otro modo estaríamos declarando una función que devuelve un puntero al tipo especificado y que admite la lista de parámetros indicada.

No tiene sentido declarar variables de tipo función, es decir, la sintaxis indicada, prescindiendo del '*' lo que realmente declara es un prototipo, y no es posible asignarle un valor a un prototipo, como se puede hacer con los punteros, sino que únicamente podremos definir la función.

Ejemplos:

int (*pfuncion1)(); (1)
void (*pfuncion2)(int); (2)
float *(*pfuncion3)(char*, int); (3)
void (*pfuncion4)(void (*)(int)); (4)
int (*pfuncion5[10])(int); (5)

El ejemplo 1 declara un puntero, "pfuncion1" a una función que devuelve un int y no acepta parámetros.

El ejemplo 2 declara un puntero, "pfuncion2" a una función que no devuelve valor y que acepta un parámetro de tipo int.

El ejemplo 3 a una función que devuelve un puntero a float y admite dos parámetros: un puntero a char y un int.

El 4, declara una función "pfuncion4" que no devuelve valor y acepta un parámetro. Ese parámetro debe ser un puntero a una función que tampoco devuelve valor y admite como parámetro un int.

El 5 declara un array de punteros a función, cada una de ellas devuelve un int y admite como parámetro un int.

Este otro ejemplo:

int *(pfuncionx)();

Equivale a:

int *pfuncionx();

Que, claramente, es una declaración de un prototipo de una función que devuelve un puntero a int y no admite parámetros.

Utilidad de los punteros a funciones

La utilidad de los punteros a funciones se manifiesta sobre todo cuando se personalizan ciertas funciones de biblioteca. Podemos por ejemplo, diseñar una función de biblioteca que admita como parámetro una función, que debe crear el usuario (en este caso otro programador), para que la función de biblioteca complete su funcionamiento.

Este es el caso de la función qsort, declarada en cstdlib. Si nos fijamos en su prototipo:

void qsort(void *base, size_t nmemb, size_t tamanyo,
   int (*comparar)(const void *, const void *));

Vemos que el cuarto parámetro es un puntero a una función comparar que devuelve un int y admite dos parámetros de tipo puntero genérico.

Esto permite a la biblioteca cstdib definir una función para ordenar arrays independientemente de su tipo, ya que para comparar elementos del array se usa una función definida por el usuario, y qsort puede invocarla después.

Asignación de punteros a funciones

Una vez declarado uno de estos punteros, se comporta como una variable cualquiera, podemos por lo tanto, usarlo como parámetro en funciones, o asignarle valores, por supuesto, del mismo tipo.

int funcion();
...
   int (*pf1)();  // Puntero a función sin argumentos 
                  // que devuelve un int.
   pf1 = funcion; // Asignamos al puntero pf1 la 
                  // función "funcion"
...
int funcion() {
   return 1;
}

La asignación es tan simple como asignar el nombre de la función.

Nota: Aunque muchos compiladores lo admiten, no es recomendable aplicar el operador de dirección (&) al nombre de la función
pf1 = &funcion;.
La forma propuesta en el ejemplo es la recomendable.

Llamadas a través de un puntero a función

Para invocar a la función usando el puntero, sólo hay que usar el identificador del puntero como si se tratase de una función. En realidad, el puntero se comporta exactamente igual que un "alias" de la función a la que apunta.

int x = pf1();

De este modo, llamamos a la función "funcion" previamente asignada a *pf1.

Ejemplo completo:

#include <iostream>
using namespace std;

int Muestra1();
int Muestra2();
int Muestra3();
int Muestra4();

int main() {
   int (*pf1)();  
   // Puntero a función sin argumentos que devuelve un int.
   int num;
   
   do {
      cout << "Introduce un número entre 1 y 4, "
           << "0 para salir: ";
      cin >> num;
      if(num >= 1 && num <=4) {
         switch(num) {
            case 1: 
               pf1 = Muestra1;
               break;
            case 2: 
               pf1 = Muestra2;
               break;
            case 3: 
               pf1 = Muestra3;
               break;
            case 4: 
               pf1 = Muestra4;
               break;
         }
         pf1();
      }
   } while(num != 0);
   
   return 0;
}

int Muestra1() {
   cout << "Muestra 1" << endl;
   return 1;
}

int Muestra2() {
   cout << "Muestra 2" << endl;
   return 2;
}

int Muestra3() {
   cout << "Muestra 3" << endl;
   return 3;
}

int Muestra4() {
   cout << "Muestra 4" << endl;
   return 4;
}

Otro ejemplo:

#include <iostream>
using namespace std;
 
int Fun1(int);
int Fun2(int);
int Fun3(int);
int Fun4(int);

int main() {
   int (*pf1[4])(int);  // Array de punteros a función con un 
                        // argumento int que devuelven un int.
   int num;
   int valores;

   pf1[0] = Fun1;
   pf1[1] = Fun2;
   pf1[2] = Fun3;
   pf1[3] = Fun4;
   do {
      cout << "Introduce un número entre 1 y 4, "
           << "0 para salir: ";
      cin >> num;
      if(num >= 1 && num <=4) {
         cout << "Introduce un número entre 1 y 10: ";
         cin >> valores;
         if(valores > 0 && valores < 11)
            pf1[num-1](valores);
      }
   } while(num != 0);
   
   return 0;
}

int Fun1(int v) {
   while(v--) cout << "Muestra 1" << endl;
   return 1;
}

int Fun2(int v) {
   while(v--) cout << "Muestra 2" << endl;
   return 2;
}

int Fun3(int v) {
   while(v--) cout << "Muestra 3" << endl;
   return 3;
}

int Fun4(int v) {
   while(v--) cout << "Muestra 4" << endl;
   return 4;
}

Palabras reservadas usadas en este capítulo

inline.

Comentarios de los usuarios (7)

Adri
2015-01-03 02:37:48

Solo por curiosidad: ¿Se pueden usar funciones como parametros de otras funciones?

Algo asi como:

include <iostream>
using namespace std;
int A(int B()){
B()
}
int C(int n){
return n;
}
int main(){
A(C(3));
return 0;
}

Me da errores, por supuesto. Pero aun asi, ¿Se puede hacer algo parecido?¿Existe alguna sintaxis concreta para hacer este tipo de cosas?

Steven R. Davidson
2015-01-03 11:48:40

Hola Adri,

Sí; usando un puntero a una función, como hemos mostrado al comienzo de este tema. Te pongo otro ejemplo,

int A( int (*B)() )
{
  return B();
}
...
A( C );

Como puedes ver, no invocamos a 'C()' directamente, sino que sólo pasamos 'C' como parámetro. De lo contrario, estaríamos invocando a 'C()' obteniendo un resultado de tipo 'int' el cual pasamos a 'A()'. Al no existir un prototipo de 'A()' que reciba un entero, se produciría un error.

Normalmente, usaríamos 'typedef' para definir el tipo del puntero a función; o sea,

typedef int (*pf)();

int A( pf B )
{
  return B();
}
...
A( C );

Así organizamos mejor el código fuente, para que sea más legible.

Hasta pronto,

Steven

Kaneda
2015-03-07 06:05:40

Una pregunta, por pura curiosidad: ¿Podría lograrse que un puntero genérico apunte a una función mediante un casting?

Saludos.

Steven R. Davidson
2015-03-18 22:28:53

Hola Kaneda,

Sí; se puede. De hecho, si lo intentas, funciona. Al fin y al cabo, una función es una dirección de memoria al comienzo de las instrucciones del cuerpo de tal función. Como los punteros manejan direcciones de memoria, tanto de datos como de funciones, no es un gran salto para un puntero apuntar a otra cosa. Por ejemplo,

#include <iostream>

using namespace std;

typedef int (*PF)(char);

int func( char c )
{
  return 10 * c;
}

int main()
{
  void *ptr = (void *)func;
  int n = ((PF)ptr)(' ');

  cout << n << endl;

  return 0;
}

Aparecerá en pantalla, 320.

Espero que esto te oriente.

Steven

manuel esteban
2015-06-13 04:51:49

gracias por ser tan claro en las explicaciones. la verdad es que hay algunas cosas que no entiendo de punteros en clase y tu me ayudas bastante.saludos

Federico
2017-07-04 15:06:54

Se puede apunta a distintas funciones que manejen una distinta cantidad y tipo de parametros con el mismo puntero. claro que no todas a la ves.?

Steven R. Davidson
2017-07-04 18:08:40

Hola Federico,

Sí, pero hay que realizar cástings. Por ejemplo,

typedef void (*PF_GENERICO)();
typedef int (*PF_1)(int);
typedef int (*PF_2)(int,int);

int neg(int a);
int suma(int a, int b);

int main()
{
  PF_GENERICO pf = reinterpret_cast<PF_GENERICO>( neg );
  int res1 = reinterpret_cast<PF_1>( pf )( 5 );
  ...
  pf = reinterpret_cast<PF_GENERICO>( suma );
  int res2 = reinterpret_cast<PF_2>( pf )( 5, 6 );
  ...
}

Podemos usar 'pf' como un puntero genérico a cualquier función, ya que guarda una dirección de memoria, pero a la hora de usar la función - invocarla - tenemos que realizar el cásting al tipo correcto.

Espero que esto aclare la duda.

Steven