Ricerca…


introduzione

I puntatori di funzione sono puntatori che puntano a funzioni invece di tipi di dati. Possono essere utilizzati per consentire la variabilità della funzione che deve essere chiamata, in fase di esecuzione.

Sintassi

  • returnType (* nome) (parametri)

  • typedef returnType (* nome) (parametri)

  • typedef returnType Name (parametri);
    Nome * nome;

  • typedef returnType Name (parametri);
    typedef Nome * NamePtr;

Assegnazione di un puntatore funzione

#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;
}

Restituzione dei puntatori funzione da una funzione

#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;
}

Migliori pratiche

Usando typedef

Potrebbe essere utile usare un typedef invece di dichiarare il puntatore funzione ogni volta a mano.

La sintassi per la dichiarazione di typedef per un puntatore a funzione è:

typedef returnType (*name)(parameters);

Esempio:

Positivo che abbiamo una funzione, sort , che si aspetta un puntatore a una funzione compare tale che:

compare - Una funzione di confronto per due elementi che deve essere fornita ad una funzione di ordinamento.

"compare" dovrebbe restituire 0 se i due elementi sono considerati uguali, un valore positivo se il primo elemento passato è "più grande" in un certo senso rispetto al secondo elemento e altrimenti la funzione restituisce un valore negativo (il che significa che il primo elemento è "minore" rispetto al secondo).

Senza un typedef passeremmo un puntatore a funzione come argomento a una funzione nel modo seguente:

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

Con un typedef , scriveremmo:

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

e quindi potremmo cambiare la firma della funzione di sort a:

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

entrambe le definizioni di sort accettano qualsiasi funzione della forma

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

I puntatori di funzione sono l'unico posto in cui devi includere la proprietà del puntatore del tipo, ad esempio non provare a definire tipi come typedef struct something_struct *something_type . Ciò vale anche per una struttura con membri a cui non si deve accedere direttamente dai chiamanti API, ad esempio il tipo FILE stdio.h (che come si vedrà ora non è un puntatore).

Prendendo indicatori di contesto.

Un puntatore a funzione dovrebbe quasi sempre prendere un vuoto fornito dall'utente * come puntatore del contesto.

Esempio

/* 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);       
}

L'uso del puntatore del contesto significa che i parametri aggiuntivi non devono essere codificati nella funzione puntata o richiedono l'uso globale.

La funzione di libreria qsort() non segue questa regola e spesso si può andare via senza contesto per funzioni di confronto banali. Ma per qualcosa di più complicato, il puntatore del contesto diventa essenziale.


Guarda anche

Funzioni puntatori

introduzione

Proprio come char e int , una funzione è una caratteristica fondamentale di C. Come tale, è possibile dichiarare un puntatore a uno: il che significa che è possibile passare la funzione da chiamare a un'altra funzione per aiutarla a svolgere il proprio lavoro. Ad esempio, se disponi di una funzione graph() che visualizza un grafico, puoi passare la funzione da rappresentare graficamente in 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

Quindi il codice sopra riportato graficherà su qualunque funzione tu abbia passato in esso - purché tale funzione soddisfi determinati criteri: cioè, che tu superi un double e ottieni un double . Ci sono molte funzioni come questa: sin() , cos() , tan() , exp() ecc. - ma ce ne sono molte che non lo sono, come graph() stesso!

Sintassi

Quindi, come si specificano le funzioni che è possibile passare in graph() e quali non è possibile? Il modo convenzionale consiste nell'utilizzare una sintassi che potrebbe non essere facile da leggere o comprendere:

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

Il problema sopra è che ci sono due cose che cercano di essere definite allo stesso tempo: la struttura della funzione e il fatto che si tratta di un puntatore. Quindi, dividi le due definizioni! Ma usando typedef , è possibile ottenere una sintassi migliore (più facile da leggere e capire).

Mnemonico per scrivere puntatori di funzioni

Tutte le funzioni C sono in effetti puntatori a un punto nella memoria del programma in cui esiste un codice. L'uso principale di un puntatore a funzione è quello di fornire un "callback" ad altre funzioni (o per simulare classi e oggetti).

La sintassi di una funzione, come definito più avanti in questa pagina è:

returnType (* nome) (parametri)

Un mnemonico per scrivere una definizione di puntatore a funzione è la seguente procedura:

  1. Inizia scrivendo una dichiarazione di una funzione normale: returnType name(parameters)
  2. Avvolgi il nome della funzione con la sintassi del puntatore: returnType (*name)(parameters)

Nozioni di base

Proprio come si può avere un puntatore a int , char , float , array / string , struct , ecc. - si può avere un puntatore a una funzione.

La dichiarazione del puntatore assume il valore di ritorno della funzione , il nome della funzione e il tipo di argomenti / parametri che riceve .

Supponi di avere la seguente funzione dichiarata e inizializzata:

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

È possibile dichiarare e inizializzare un puntatore a questa funzione:

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

Se disponi di una funzione di annullamento, potrebbe avere il seguente aspetto:

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

Quindi dichiarare il puntatore ad esso sarebbe:

void (*functionPtrPrint)(void) = Print;

Accedere alla funzione stessa richiederebbe il dereferenziamento del puntatore:

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

Come visto in esempi più avanzati in questo documento, dichiarare un puntatore a una funzione potrebbe diventare disordinato se la funzione è passata più di pochi parametri. Se hai alcune indicazioni su funzioni che hanno "struttura" identica (stesso tipo di valore di ritorno e lo stesso tipo di parametri) è meglio usare il comando typedef per risparmiare qualche digitazione e per rendere il codice più chiaro:

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;
}

È inoltre possibile creare una matrice di puntatori di funzioni . Se tutti i puntatori sono della stessa "struttura":

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

Puoi saperne di più qui e qui .

È anche possibile definire una serie di puntatori di funzioni di tipi diversi, sebbene ciò richiederebbe la trasmissione ogni volta che si desidera accedere alla funzione specifica. Puoi saperne di più qui .



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow