Buscar..


Introducción

Los punteros de función son punteros que apuntan a funciones en lugar de tipos de datos. Se pueden usar para permitir la variabilidad en la función que se va a llamar, en tiempo de ejecución.

Sintaxis

  • returnType (* nombre) (parámetros)

  • typedef returnType (* nombre) (parámetros)

  • typedef returnType Name (parámetros);
    Nombre nombre;

  • typedef returnType Name (parámetros);
    typedef Nombre * NamePtr;

Asignar un puntero de función

#include <stdio.h>

/* increment: take number, increment it by one, and return it */
int increment(int i)
{
    printf("increment %d by 1\n", i);
    return i + 1;
}

/* decrement: take number, decrement it by one, and return it */
int decrement(int i)
{
    printf("decrement %d by 1\n", i);
    return i - 1;
}

int main(void)
{
    int num = 0;          /* declare number to increment */
    int (*fp)(int);       /* declare a function pointer */

    fp = &increment;      /* set function pointer to increment function */
    num = (*fp)(num);     /* increment num */
    num = (*fp)(num);     /* increment num a second time */

    fp = &decrement;      /* set function pointer to decrement function */
    num = (*fp)(num);     /* decrement num */
    printf("num is now: %d\n", num);
    return 0;
}

Devolviendo punteros a funciones desde una función

#include <stdio.h>

enum Op
{
  ADD = '+',
  SUB = '-',
};


/* add: add a and b, return result */    
int add(int a, int b)
{
    return a + b;
}

/* sub: subtract b from a, return result */
int sub(int a, int b)
{
    return a - b;
}

/* getmath: return the appropriate math function */
int (*getmath(enum Op op))(int,int)
{
    switch (op)
    {
        case ADD:
            return &add;
        case SUB:
            return &sub;
        default:
            return NULL;
    }
}

int main(void)
{
    int a, b, c;
    int (*fp)(int,int);

    fp = getmath(ADD);

    a = 1, b = 2;
    c = (*fp)(a, b);
    printf("%d + %d = %d\n", a, b, c);
    return 0;
}

Mejores prácticas

Usando typedef

Puede ser útil usar un typedef lugar de declarar el puntero a la función cada vez que esté a mano.

La sintaxis para declarar un typedef para un puntero de función es:

typedef returnType (*name)(parameters);

Ejemplo:

Dice que tenemos una función, una sort , que espera un puntero de función a una función de compare tal que:

compare: una función de comparación para dos elementos que se debe suministrar a una función de clasificación.

se espera que "compare" devuelva 0 si los dos elementos se consideran iguales, un valor positivo si el primer elemento pasado es "más grande" en cierto sentido que el último elemento y, de lo contrario, la función devuelve un valor negativo (lo que significa que el primer elemento es "menor" que este último).

Sin un typedef , typedef un puntero a una función como un argumento a una función de la siguiente manera:

void sort(int (*compare)(const void *elem1, const void *elem2)) { 
    /* inside of this block, the function is named "compare" */
}

Con un typedef , escribiríamos:

typedef int (*compare_func)(const void *, const void *);

y luego podríamos cambiar la firma de función de sort a:

void sort(compare_func func) { 
    /* In this block the function is named "func" */
}

Ambas definiciones de sort aceptarían cualquier función de la forma.

int compare(const void *arg1, const void *arg2) {
    /* Note that the variable names do not have to be "elem1" and "elem2" */
}

Los punteros de función son el único lugar donde debe incluir la propiedad de puntero del tipo, por ejemplo, no intente definir tipos como typedef struct something_struct *something_type . Esto se aplica incluso para una estructura con los miembros que no se supone que se accede directamente por las personas que llaman API, por ejemplo, el stdio.h FILE tipo (que ya que ahora se dará cuenta de que no es un puntero).

Tomando punteros de contexto.

Un puntero de función casi siempre debe tomar un void * proporcionado por el usuario como un puntero de contexto.

Ejemplo

/* function minimiser, details unimportant */
double findminimum( double (*fptr)(double x, double y, void *ctx), void *ctx)
{
    ...
    /* repeatedly make calls like this */
    temp = (*fptr)(testx, testy, ctx);
}

/* the function we are minimising, sums two cubics */
double *cubics(double x, double y, void *ctx)
{
    double *coeffsx = ctx;
    double *coeffsy = coeffx + 4;

    return coeffsx[0] * x * x * x + coeffsx[1] * x * x + coeffsx[2] * x + coeffsx[3] +
           coeffsy[0] * y * y * y + coeffsy[1] * y * y + coeffsy[2] * y + coeffsy[3];

} 

void caller()
{
    /* context, the coefficients of the cubics */
    double coeffs[8] = {1, 2, 3, 4, 5, 6, 7, 8};
    double min;

    min = findminimum(cubics, coeffs);       
}

El uso del puntero de contexto significa que los parámetros adicionales no necesitan ser codificados en la función apuntada, o requieren el uso de elementos globales.

La función de biblioteca qsort() no sigue esta regla, y a menudo se puede escapar sin contexto para funciones de comparación triviales. Pero para algo más complicado, el puntero de contexto se vuelve esencial.


Ver también

Punteros funciones

Introducción

Al igual que char e int , una función es una característica fundamental de C. Como tal, puede declarar un puntero a uno: lo que significa que puede pasar qué función llamar a otra función para ayudarlo a hacer su trabajo. Por ejemplo, si tenía una función graph() que mostraba un gráfico, podría pasar qué función graficar en graph() .

// A couple of external definitions to make the example clearer
extern unsigned int screenWidth;
extern void plotXY(double x, double y);

// The graph() function.
// Pass in the bounds: the minimum and maximum X and Y that should be plotted.
// Also pass in the actual function to plot.
void graph(double minX, double minY,
           double maxX, double maxY,
           ???? *fn) {            // See below for syntax

    double stepX = (maxX - minX) / screenWidth;
    for (double x=minX; x<maxX; x+=stepX) {

        double y = fn(x);         // Get y for this x by calling passed-in fn()

        if (minY<=y && y<maxY) {
            plotXY(x, y);         // Plot calculated point
        } // if
    } for
} // graph(minX, minY, maxX, maxY, fn)

Uso

Por lo tanto, el código anterior representará gráficamente cualquier función que le haya asignado, siempre y cuando esa función cumpla con ciertos criterios: a saber, que pase un double y obtenga un double . Hay muchas funciones como esa - sin() , cos() , tan() , exp() etc. - ¡pero hay muchas que no lo son, como la graph() sí misma!

Sintaxis

Entonces, ¿cómo especifica qué funciones puede pasar a graph() y cuáles no? La forma convencional es mediante el uso de una sintaxis que puede no ser fácil de leer o entender:

double (*fn)(double); // fn is a pointer-to-function that takes a double and returns one

El problema anterior es que hay dos cosas que se deben definir al mismo tiempo: la estructura de la función y el hecho de que es un puntero. Entonces, ¡divide las dos definiciones! Pero al usar typedef , se puede lograr una mejor sintaxis (más fácil de leer y entender).

Mnemónicos para escribir punteros a funciones.

Todas las funciones de C están en punteros de actualidad a un punto en la memoria del programa donde existe algún código. El uso principal de un puntero de función es proporcionar una "devolución de llamada" a otras funciones (o para simular clases y objetos).

La sintaxis de una función, tal como se define más adelante en esta página es:

returnType (* nombre) (parámetros)

Un mnemónico para escribir una definición de puntero de función es el siguiente procedimiento:

  1. Comience escribiendo una declaración de función normal: returnType name(parameters)
  2. Ajuste el nombre de la función con la sintaxis del puntero: returnType (*name)(parameters)

Lo esencial

Al igual que puede tener un puntero a un int , char , float , array / string , struct , etc., puede tener un puntero a una función.

La declaración del puntero toma el valor de retorno de la función , el nombre de la función y el tipo de argumentos / parámetros que recibe .

Digamos que tienes la siguiente función declarada e inicializada:

int addInt(int n, int m){
    return n+m;
}

Puede declarar e inicializar un puntero a esta función:

int (*functionPtrAdd)(int, int) = addInt; // or &addInt - the & is optional

Si tienes una función de vacío, podría verse así:

void Print(void){
    printf("look ma' - no hands, only pointers!\n");
}

Entonces declarar el puntero a él sería:

void (*functionPtrPrint)(void) = Print;

Acceder a la función en sí requeriría desreferenciar el puntero:

sum = (*functionPtrAdd)(2, 3); //will assign 5 to sum
(*functionPtrPrint)(); //will print the text in Print function

Como se ve en los ejemplos más avanzados de este documento, declarar un puntero a una función podría ensuciarse si la función pasa más que unos pocos parámetros. Si tiene algunos punteros a funciones que tienen una "estructura" idéntica (el mismo tipo de valor de retorno y el mismo tipo de parámetros) es mejor usar el comando typedef para ahorrar algo de escritura y para aclarar el código:

typedef int (*ptrInt)(int, int);

int Add(int i, int j){
    return i+j;
}

int Multiply(int i, int j){
    return i*j;
}

int main()
{
    ptrInt ptr1 = Add;
    ptrInt ptr2 = Multiply;

    printf("%d\n", (*ptr1)(2,3)); //will print 5
    printf("%d\n", (*ptr2)(2,3)); //will print 6
    return 0;
}

También puede crear una matriz de punteros de función . Si todos los punteros son de la misma "estructura":

int (*array[2]) (int x, int y); // can hold 2 function pointers
array[0] = Add;
array[1] = Multiply;

Puedes aprender más aquí y aquí .

También es posible definir una matriz de punteros de función de diferentes tipos, aunque eso requeriría la conversión cuando quiera que quiera acceder a la función específica. Puedes aprender más aquí .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow