Les pointeurs de fonctions

Dans certains cas il est commode de pouvoir passer une fonction comme paramètre à une autre fonction. Quelques exemples: Parfois il aussi aussi commode de pouvoir utiliser de tableaux de fonctions. Par exemple si on affiche un menu où l'utilisateur choisit entre plusieurs actions et chaque action est implémentée par une fonction et si nous avons un tableau de fonctions fon nous pouvons exécuter l'action i par fon[i](param).

Déclaration de variables de type pointeur de fonction

Une variable pf de type pointeur de fonction se déclare par
type_de_retour (*pf)(liste_de_paramètres);
Par exemple
double (*pf)(double, char *);
déclare une variable pf: «pointeur de fonction à deux paramètres double et char * et qui retourne une valeur double ». Notez où se trouve le nom de la variable pf dans cette déclaration et les parenthèses autour de *pf. Ces parenthèses sont nécessaires, si on les supprime on obtient:
double *pf(double, char *);
c'est-à-dire un prototype de fonction où pf n'est plus une variable mais le nom d'une fonction définie quelque part ailleurs dans le programme.
Un autre exemple
char *(*pd)(char *, double, int);
déclare une variable pd : pointeur de fonction retournant char * (retournant un pointeur de char) avec trois paramètres: char *, double et int. La variable pd ci-dessus n'est pas initialisée. Une fois déclarée, nous pouvons lui affecter NULL:
pd = NULL;
Comme pour d'autres variables nous pouvons effectuer une initialisation au moment de la déclaration:
char *(*pd)(char *, double, int) = NULL;
déclare un pointeur de fonction et initialise pd avec la valeur NULL.
Si dans notre programme nous avons des (définitions de) fonctions f et g:
char * f(char *s, double, int i){
/* definition de f */
}

char * g(char *t, double, int j){
/* definition de g */
}
les deux avec la même signature (paramètres et la valeur de retour) que dans la déclaration de pd alors nous pouvons écrire par exemple
 
int i;
char *s;
char *(*pd)(char *, double, int);

if( i == 0 )
   pd = f;
else
   pd = g;

s = pd("toto", 12.4, 5);   
Les instructions
pd = f;
et
pd = g;
affectent à la variable pd soit un pointeur vers f soit un pointeur vers g. L'instruction
s = pd("toto", 12.4, 5);
donnera lieu à un appel soit à la fonction f soit à la fonction g, en fonction de la valeur de la variable pd. Notez que dans l'instruction
pd = f;
pd est une variable tandis que f joue rôle d'une constante qui sera affectée à pd.

Définition de type «pointeur de fonction»

Un code qui utilise beaucoup de pointeurs de fonctions devient vite illisible. Dans
#include <math.h>
double (*tab[])(double) = {sin, cos, sinh, sqrt, tan};
pouvez vous déchiffrer ce que c'est tab? En fait, c'est une déclaration d'un tableau de pointeurs de fonctions, chacune de ces fonction prend un double en paramètre et retourne aussi un double. Le tableau tab est initialisé avec 5 valeurs: sin, cos, sinh, sqrt, tan, ce sont des fonctions mathématiques de la bibliothèque de C. Et maintenant
double d = tab[3](5.3);
fait appel à la fonction tab[3], c'est-à-dire sqrt, avec l'argument 5.3. Mais le tableau tab ci-dessus est plus facile à déclarer si on définit auparavant le type des éléments de tab:
#include <math.h>
typedef double (*p_fonct)(double);
p_fonct tab[] = {sin, cos, sinh, sqrt, tan};
Ici on commence par déclarer le type p_fonct pointeur de fonction et ensuite un tableau tab des éléments du type p_fonct initialisé comme précédemment avec 5 fonctions mathématiques. Notez la place du nom de type p_fonct dans typedef: à la même position que le nom de variable dans une déclaration d'une variable «pointeur de fonctions».

La fonction qsort

La fonction qsort de C standard déclarée dans stdlib.h implémente le tri rapide (quick sort).
void qsort(void *,  /* tableau */
           size_t,  /* nombre d'element du tableau */
           size_t,  /* taille d'un element en octets */
           int (*)(const void *, const void *));
Le premier paramètre est utilisé pour passer l'adresse du premier élément d'un tableau à trier. Le deuxième paramètre de qsort sert à passer le nombre d'éléments de ce tableau et le troisième c'est la taille d'un élément.
Le dernier paramètre est un pointeur de fonction. Cette fonction permet de comparer les éléments du tableau. Elle prend comme paramètre les pointeurs vers deux éléments à comparer, a et b, et retourne un entier inférieur à 0 si a<b, 0 si a = b et un entier supérieur à 0 si a>b.
Exemple
Si
int tab[100];
est un tableau d'entiers alors pour le trier avec la fonction qsort il nous faut écrire une fonction de comparaison
int cmp_int(const void *x, const void *y){
  int a=  *(int *)x;
  int b=  *(int *)y;
  return a - b;
}
Notez que x et y sont des pointeurs vers les données à comparer mais dans notre cas il s'agit d'un tableau d'entiers alors x et y sont en fait de pointeurs vers int. Alors avec le cast (int *)x nous obtenons les pointeurs vers int et en appliquant * nous récupérons les valeurs int à comparer.
Et maintenaant nous pouvons trier tab
qsort( tab,  /* tableau */
       sizeof(tab)/sizeof(tab[0]), /* le nombre d'elemnts */
       sizeof(tab[0]), /* la taille d'un element */
       cmp_int);    /* la fonction qui compare les elements */

Exemple Si nous voulons trier un tableau de chaînes de caractères en utilisant qsort il nous faut une fonction de comparaison:
int cmp_strings(const void *x, const void * y){
  return strcmp(* ((char **)x),  *((char **)y));
}
Les données à trier étant de type char * (chaînes de caractères en C) et les paramètres x et y étant des pointeurs vers les données, (c'est-à-dire char **) nous commençons par un cast ((char **)x) et ((char **)y) et nous appliquant l'opérateur * pour retrouver deux chaînes de caractères qui sont comparées avec la fonction de C standard strcmp.
Maintenant nous pouvons trier les paramètres de main

int main(int argc, char *argv[]){

qsort( &argv[1],   /* l'adresse du premier element  */
       argc - 1,   /* nombre d'elements             */
       sizeof(char *), /* taille d'un element       */
       cmp_strings);   /* la fonction utilisee pour comparer les elements */
Attention il est impossible de faire passer strcmp à la place de notre fonction cmp_strings dans qsort et cela pour deux raisons.

Utilisation des paramètres «pointeur de fonction» dans une fonction

La fonction
int
cmp_fon( double (*f)(double),
         double (*g)(double),
         double x){

  return  ( f(x) + g(x) )/ 2;
}
prend comme paramètres deux pointeurs de fonctions f et g et un nombre double. Nous pouvons voir qu'à l'intérieur de cmp_fon f et g sont utilisés comme des fonctions.