C Language
Pointeurs de fonction
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 ⊂
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
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:
- Commencez par écrire une déclaration de fonction normale:
returnType name(parameters)
- 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 .