Recherche…


Introduction

Les pointeurs de fonction sont des pointeurs qui pointent vers des fonctions plutôt que des types de données. Ils peuvent être utilisés pour autoriser la variabilité de la fonction à appeler lors de l'exécution.

Syntaxe

  • returnType (* nom) (paramètres)

  • typedef returnType (* name) (paramètres)

  • typedef returnType Name (paramètres);
    Nom nom;

  • typedef returnType Name (paramètres);
    typedef Nom * NomPtr;

Assigner un pointeur de fonction

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

Renvoi de pointeurs de fonction à partir d'une fonction

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

Les meilleures pratiques

Utiliser typedef

Il peut être utile d'utiliser un typedef au lieu de déclarer le pointeur de fonction à la main.

La syntaxe pour déclarer un typedef pour un pointeur de fonction est la suivante:

typedef returnType (*name)(parameters);

Exemple:

Posit que nous avons une fonction, sort , qui attend un pointeur de fonction sur une fonction compare telle que:

compare - Une fonction de comparaison pour deux éléments à fournir à une fonction de tri.

"compare" est censé renvoyer 0 si les deux éléments sont considérés égaux, une valeur positive si le premier élément passé est "plus grand" dans un certain sens que le dernier élément et sinon la fonction retourne une valeur négative (ce qui signifie que le premier élément est "moindre" que celui-ci).

Sans typedef nous typedef un pointeur de fonction comme argument à une fonction de la manière suivante:

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

Avec un typedef , on écrirait:

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

et puis nous pourrions changer la signature de la fonction de sort en:

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

les deux définitions de sort accepteraient n'importe quelle fonction de la forme

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

Les pointeurs de fonction sont le seul endroit où vous devez inclure la propriété de pointeur du type, par exemple, n'essayez pas de définir des types comme typedef struct something_struct *something_type . Cela vaut même pour une structure avec des membres qui ne sont pas censés accéder directement aux appelants de l'API, par exemple le type de FILE stdio.h (qui, comme vous le constaterez maintenant, n'est pas un pointeur).

Prendre des pointeurs de contexte.

Un pointeur de fonction devrait presque toujours prendre un vide * fourni par l'utilisateur comme pointeur de contexte.

Exemple

/* 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'utilisation du pointeur de contexte signifie que les paramètres supplémentaires n'ont pas besoin d'être codés en dur dans la fonction pointée ou nécessitent l'utilisation de globales.

La fonction de bibliothèque qsort() ne suit pas cette règle et on peut souvent s’en sortir sans contexte pour des fonctions de comparaison triviales. Mais pour quelque chose de plus compliqué, le pointeur de contexte devient essentiel.


Voir également

Fonctions pointeurs

introduction

Tout comme char et int , une fonction est une caractéristique fondamentale de C. En tant que tel, vous pouvez déclarer un pointeur sur un: cela signifie que vous pouvez passer à quelle fonction appeler une autre fonction pour l'aider à faire son travail. Par exemple, si vous aviez une fonction graph() qui affichait un graphique, vous pouviez transmettre cette fonction à 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)

Usage

Donc, le code ci-dessus représentera graphiquement les fonctions que vous y avez passées - à condition que cette fonction réponde à certains critères: à savoir que vous transmettiez un double et obteniez un double . Il y a beaucoup de fonctions comme celle-ci - sin() , cos() , tan() , exp() etc. - mais il y en a beaucoup qui ne le sont pas, comme graph() lui-même!

Syntaxe

Alors, comment spécifiez-vous les fonctions que vous pouvez passer dans graph() et celles que vous ne pouvez pas? La méthode conventionnelle consiste à utiliser une syntaxe difficile à lire ou à comprendre:

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

Le problème ci-dessus est que deux choses essaient d'être définies en même temps: la structure de la fonction et le fait que c'est un pointeur. Alors, divisez les deux définitions! Mais en utilisant typedef , une meilleure syntaxe (plus facile à lire et à comprendre) peut être obtenue.

Mnémonique pour écrire des pointeurs de fonction

Toutes les fonctions C sont en fait des pointeurs vers un point de la mémoire du programme où du code existe. L'utilisation principale d'un pointeur de fonction consiste à fournir un "rappel" à d'autres fonctions (ou à simuler des classes et des objets).

La syntaxe d'une fonction, définie plus bas sur cette page, est la suivante:

returnType (* nom) (paramètres)

Un mnémonique pour écrire une définition de pointeur de fonction est la procédure suivante:

  1. Commencez par écrire une déclaration de fonction normale: returnType name(parameters)
  2. Enveloppez le nom de la fonction avec la syntaxe du pointeur: returnType (*name)(parameters)

Les bases

Tout comme vous pouvez avoir un pointeur sur un int , char , float , array / string , struct , etc., vous pouvez avoir un pointeur sur une fonction.

La déclaration du pointeur prend la valeur de retour de la fonction , le nom de la fonction et le type d'arguments / paramètres qu'il reçoit .

Supposons que la fonction suivante soit déclarée et initialisée:

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

Vous pouvez déclarer et initialiser un pointeur sur cette fonction:

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

Si vous avez une fonction vide, cela pourrait ressembler à ceci:

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

Ensuite, déclarer le pointeur serait:

void (*functionPtrPrint)(void) = Print;

Pour accéder à la fonction elle-même, il faut déréférencer le pointeur:

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

Comme on le voit dans des exemples plus avancés de ce document, déclarer un pointeur sur une fonction peut être désordonné si la fonction est transmise avec plus de quelques paramètres. Si vous avez quelques pointeurs vers des fonctions ayant une "structure" identique (même type de valeur de retour et même type de paramètres), mieux vaut utiliser la commande typedef pour vous éviter de taper du code et rendre le code plus clair:

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

Vous pouvez également créer un tableau de pointeurs de fonctions . Si tous les pointeurs ont la même "structure":

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

Vous pouvez en apprendre plus ici et ici .

Il est également possible de définir un tableau de pointeurs de fonctions de différents types, bien que cela nécessiterait de lancer quand vous voulez accéder à la fonction spécifique. Vous pouvez en apprendre plus ici .



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow