Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Bien. Passons-nous d'un long discours, plongeons !
La question est donc :
Comment déclarer un pointeur de fonction ?
Je crois savoir ce que la plupart de ceux qui lisent ce tuto se disent : héhé, fastoche !
D'abord, il faut déclarer un pointeur de type fonction... mouais, pas si simple.
Eurêka ! Utilisons le type
void* !
Code : C
Maintenant, il faut lui donner l'adresse de la... heu... Mais ça a une adresse, une fonction ?
Oui, bien sûr, sinon on ne parlerait pas de pointeurs. En fait lorsqu'on crée un pointeur de fonction, il contiendra l'adresse de la première instruction de la fonction en question. Mais, franchement, en pratique ce n'est pas vraiment important de le savoir.
Donc, on va mettre l'opérande '
&' pour indiquer que c'est une adresse. Le programme final sera donc :
Code : C1
2 | void* p = NULL;
p = &fonction;
|
Mais ce code ne fonctionne pas.

Vous pouvez le tester...
La réaction la plus fréquente, après moult essais sera, et c'est justifié :
Citation : ZérO énervéAaaaaaarrrrgggg !
Je la comprends, c'est aussi pourquoi je fais ce tuto

.
Nous allons voir dans un premier temps comment créer un pointeur de fonction sans paramètre et ne renvoyant rien. Dans un second, on mettra des paramètres et on gérera le type de la variable retournée par la fonction.
Donc, imaginons qu'on ait une fonction nommée
fonction (super original, n'est-ce pas ?

), définie ainsi :
Code : C1
2
3
4
5 | void fonction(void)
{
printf("Le programme fonctionne. \n");
getchar();
}
|
Certes, elle ne sert à rien, mais elle est parfaite pour la théorie.
Maintenant essayons d'imaginer pourquoi notre code de tout à l'heure ne fonctionnait pas. Tout simplement parce que lorsqu'on écrit :
Code : C
...pourquoi
p serait-il un pointeur sur une fonction et non pas autre chose ?
Il faut donc dans un premier temps indiquer que c'est un pointeur de fonction. Pour cela, on mettra des parenthèses à côté de lui, comme ceci :
Code : C
...où devront être indiqués les paramètres effectifs de la fonction pointée.
Mais, au fait, pourquoi y a-t-il des parenthèses autour de
*p ?
C'est une question de priorité (merci rz0). Je me permets ici de citer le K&R :
Citation : K&R, 5.11* est un opérande utilisé en préfixe et il a une priorité plus faible que () ; c'est pourquoi les parenthèses sont nécessaires pour forcer l'association correcte.
fonction ne prend pas d'arguments, c'est pourquoi on ne met rien dans les parenthèses.
Et voilà ! Notre pointeur est déclaré, manque plus qu'à le définir :
Code : C
Donc, le code final donne :
Code : C1
2 | void (*p)();
p = &fonction;
|
Ça y est, nous voilà arrivés au bout de nos peines. Je peux vous assurer qu'à partir de là le reste est vraiment simple

.
Par exemple, imaginons maintenant que
fonction2 soit définie comme suit :
Code : C1
2
3
4 | int fonction2(int a, int b)
{
return a += b;
}
|
Le code va un peu changer. On va devoir déclarer le pointeur de type
int, puisque la fonction renvoie un
int.
On aura donc :
Code : C1
2 | int (*p)(int, int); /* Avec les types des paramètres */
p = &fonction2;
|
Ça peut paraître bizarre au début mais on s'y fait vite.
Voilà, vous savez donc créer un pointeur de fonction !
Mais avant de passer à la suite, il faut que je vous sachiez que :
Citation : rz0 dans le topic 'notion de C'Un nom de fonction est automatiquement converti en pointeur sur cette fonction, il n'y a pas besoin du & mais c'est permis et il n'y a pas vraiment de différence.
...C'est on ne peut plus clair. Du coup, on peut enlever le
& lors de la définition du pointeur.
Code : C
... est donc semblable à :
Code : C
Cependant, je trouve que la présence du '
&' permet de bien voir qu'il s'agit d'une adresse. C'est donc à vous de voir, mais je vous conseille de le mettre, ne serait-ce que par souci de compréhension.
Enfin maintenant, nous pouvons passer à la suite...
Dans cette sous-partie, nous verrons :
- la déférenciation du pointeur
- le passage d'une fonction dans une autre via un pointeur
- la création d'un tableau de pointeurs de fonctions
- les pointeurs de fonction et les structures.
On aura à peu près fait le tour.
La déférenciation du pointeur
Vous allez voir que si vous avez compris la partie précédente, vous n'aurez aucun mal ici.
Nous allons reprendre pour exemple nos deux fonctions précédentes
fonction et
fonction2.
Donc, pour déférencer un pointeur de variable, vous savez qu'on utilise l'opérateur d'indirection '
*'. Pour un pointeur de fonction, que va-t-on utiliser, d'après vous ?
Allez, cherchez un peu quand même.
Bien vu ! (ou pas

) La réponse était (
*...).
Toujours question de la règle des priorités, à laquelle j'ai fait appel plus haut.
Reste encore que dans l'appel d'une fonction, on indique les variables qui seront passées en paramètre.
Le code de notre programme pour
fonction pourrait donc être :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | #include <stdio.h>
void fonction()
{
printf("Le programme fonctionne. \n");
getchar();
}
int main()
{
void (*p)() = NULL;
p = &fonction;
(*p)(); /* On appelle fonction via son pointeur. */
return 0;
}
|
Résultat :
Code : Console
Allelujah !
C'est beau, ces moments là...
Enfin nous avons gagné une bataille, mais pas encore la guerre.
Alors à l'assaut !
Déférençons maintenant notre pointeur de fonction pour
fonction2.
On aura alors, par exemple :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | #include <stdio.h>
int fonction2(int a, int b)
{
return a * b;
}
int main()
{
int a = 10, b = 20;
int (*p)(int, int) = NULL;
p = &fonction2;
printf("Resultat : %d", (*p)(a, b));
getchar();
return 0;
}
|
Code : Console
Yeeaah !
Mais c'est trop simple ! (Hé ! Je vous avais prévenus !

)
Maintenant, je voudrais vous montrer (encore

) une écriture possible.
Elle correspond tout simplement à celle acceptée par la norme ANSI, nous verrons le pourquoi de ce changement quand nous parlerons des structures. Elle stipule que cette ligne :
Code : C
... peut dorénavant s'écrire :
Code : C
Le pourquoi est un peu dur à saisir au premier abord, alors accrochez-vous, relisez deux ou trois fois s'il le faut

.
Citation : rz0, commentaires de ce mini-tutoL'opérateur d'appel de fonction () prend pour opérande à sa gauche un pointeur de fonction.
C'est la propriété même d'une fonction de se convertir implicitement en pointeur sur fonction qui fait que lorsque l'on effectue un appel avec un nom de fonction ordinaire, on utilise réellement un pointeur pour l'appel.
Conceptuellement : (*f)() effectue une double conversion : de pointeur à fonction puis de fonction à pointeur avant d'être appelé.
Ce qui veut dire que lorsqu'on fait par exemple :
Code : C
...
f est un pointeur de fonction sur la fonction
f.
C'est le même principe que pour les tableaux.
Je crois que j'ai dit tout ce que j'avais à dire sur ce point, passons à un autre qui lui est directement lié.
Le passage d'une fonction dans une autre via un pointeur
Cette partie sera très courte : si vous avez compris la précédente, elle ne devrait pas poser problème.
Ici, nous allons avoir besoin pour mon exemple d'une troisième fonction (que l'on nommera...
fonction3, pour changer). En fait, on ne se servira que de
fonction2 et
fonction3.
Imaginons donc que l'on veuille récupérer le résultat de notre fonction 2 et l'utiliser dans fonction 3, qui est définie ainsi :
Code : C1
2
3
4 | int fonction3(int (*p)(), int d)
{
return p(a, b) / d;
}
|
Rien de plus simple, on enverra le pointeur de notre fonction déférencé.
Voici mon fameux code d'exemple :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | #include <stdio.h>
int fonction2(int a, int b)
{
return a * b;
}
int fonction3(int (*p)(int, int), int a, int b, int d)
{
return p(a, b) / d;
}
int main(void)
{
int a = 20;
int b = 20;
int d = 100;
int (*p)(int, int);
p = &fonction2;
printf("Resultat : %d \n", (*p)(a, b));
printf("Resultat : %d \n", fonction3(p, a, b, d));
getchar();
return 0;
}
|
Vous devez avoir remarqué que j'ai mélangé les écritures pour vous familiariser aux deux. J'aurais tout de même pu écrire :
Code : C1 | printf("Resultat : %d \n"` fonction3((*p)(a` b)` d));
|
... mais ça fait beaucoup de parenthèses...
Avouez que ce n'était pas vraiment dur... Mais dans la dernière partie, heureusement, ça va se corser un peu (comme on dit dans le nord de la France... Bon d'accord je sors

).
La création d'un tableau de pointeurs de fonctions
Ici, rien de bien compliqué non plus, à condition d'être un peu attentifs. Après ça et le point sur les structures, vous serez vite étonnés de ce que vous serez capables de faire, qui se précisera dans la prochaine sous-partie.
Je donne le code et l'explique après si besoin :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 | #include <stdio.h>
int fonction2(int a, int b)
{
return a * b;
}
int fonction3(int c, int d)
{
return c / d;
}
int main()
{
int a = 20;
int b = 20;
int d = 100;
int (*p)(int, int) = NULL;
p = &fonction2;
int (*p2)(int, int) = NULL;
p2 = &fonction3;
int (*t[2])() = { p, p2 }; /* Tableau de pointeurs de fonctions. */
printf("Resultat : %d \n", (*p)(a, b));
printf("Resultat : %d \n", (*p2)(p(a, b), d));
printf ("\n");
printf("Resultat : %d \n", t[0](a, b)); /* On teste avec le tableau */
printf("Resultat : %d \n", t[1](p(a, b), d));
getchar();
return 0;
}
|
Code : Console | Resultat : 400
Resultat : 4
Resultat : 400
Resultat : 4 |
Toute la nouveauté est ici, même si en fait ce n'est qu'une application de la théorie décrite dans la première partie :
Code : C1 | int (*t[2])() = { p, p2 }; /* Tableau de pointeurs de fonctions. */
|
Que fait-on concrètement ?
D'abord, on indique que les fonctions retournent un entier : tableau de
int.
Ensuite, on indique qu'il s'agit de pointeurs grâce à l'étoile '*'.
Enfin, on indique que ces pointeurs pointent sur des fonctions avec ().
Quant aux autres parenthèses, je crois que vous aurez compris qu'il est encore question de priorité. Je ne m'étendrai pas dessus cinquante fois. Il existe un tableau des priorités - s'y référer si besoin - mais ici, il s'agit encore de la faible priorité de '*' (cf tableau 2-1 du K&R).
Vous noterez tout de même que dans le cas où l'une des fonctions retourne autre chose que le type indiqué lors de la création du tableau, vous devrez faire un cast lors de la définition du pointeur. Par exemple :
(long) p = f. Je ne m'y attarderai pas, à vous de faire les tests.
Les pointeurs de fonction et les structures
Au programme :
- utilisation de fonctions dans une structure.
Soyez attentifs, ouvrez votre IDE (j'espère que vous l'avez déjà fait) et let's go !
Une fonction dans une structure ? Mais c'est simple !
Pourtant, ce code ne fonctionne pas :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 | #include <stdlib.h>
#include <stdio.h>
typedef struct structure {
int fonction2(int, int);
int fonction3(int, int);
} structure;
int fonction2(int a, int b)
{
return a * b;
}
int fonction3(int c, int d)
{
return c / d;
}
int main()
{
int a = 20;
int b = 10;
int c = 30;
int d = 3;
structure operateur;
printf("%d \n", operateur.fonction2(a, b));
printf("%d \n"` operateur.fonction3(c, d));
getchar();
return 0;
}
|
Je sais, c'est un peu long mais ça illustre très bien le problème. Nous avons ces deux erreurs :
Citation : IDE5 | "fichier" | field `fonction2' declared as a function
6 | "fichier" | field `fonction3' declared as a function
En gros : loupé, essaie encore !
Pour palier ce problème, nous allons devoir utiliser les pointeurs de fonctions. Je pense que vous vous en étiez doutés...
Nous allons donc déclarer nos pointeurs
dans la structure, puis nous les affecterons dans le
main.
Aussitôt dit, aussitôt fait :
Code : C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 | #include <stdlib.h>
#include <stdio.h>
typedef struct structure {
int (*p_f2)(int, int); /* On déclare nos pointeurs. */
int (*p_f3)(int, int);
} structure;
int fonction2(int a, int b)
{
return a * b;
}
int fonction3(int c, int d)
{
return c / d;
}
int main()
{
int a = 20;
int b = 10;
int c = 30;
int d = 3;
structure operateur;
operateur.p_f2 = &fonction2;
operateur.p_f3 = fonction3; /* Sans le '&' mais c'est pareil. */
printf("%d \n", (*operateur.p_f2)(a` b));
printf("%d \n", operateur.p_f3(c` d));
getchar();
return 0;
}
|
Ce code-ci compile (merci louisclem au passage, on se comprend

).
Voilà, j'ai fait le tour de ce que je voulais vous enseigner ici de théorique, et il me semble que j'ai fait le tour en général des pointeurs de fonctions.
Pour ce qui est de la pratique, les deux travaux pratiques qui figuraient dans ma troisième partie sont obsolètes (comprendre : pas représentatifs des connaissances acquises) ; je vais tout de même tenter d'en trouver de nouveaux, vous pouvez donc revenir ici à vos moments perdus voir si un ou deux TP ne sont pas sortis

.