Programmation C-C++/Pointeurs et références de fonctions

Un livre de Wikilivres.
En cours d'importationlink={{{link}}}

Ce livre est en cours de copie depuis le site http://casteyde.christian.free.fr/online/cours_cpp/ qui le fournit sous licence GFDL.

Cours de C/C++
^
Pointeurs et références
Notion d'adresse
Notion de pointeur
Référencement, indirection
Notion de référence
Lien entre les pointeurs et les références
Passage de paramètres par variable ou par valeur
Références et pointeurs constants et volatiles
Arithmétique des pointeurs
Utilisation des pointeurs avec les tableaux
Les chaînes de caractères : pointeurs et tableaux à la fois !
Allocation dynamique de mémoire
Pointeurs et références de fonctions
Paramètres de la fonction main - ligne de commande
DANGER

Livre original de C. Casteyde

Pointeurs de fonctions[modifier | modifier le wikicode]

Il est possible de faire des pointeurs de fonctions. Un pointeur de fonction contient l'adresse du début du code binaire constituant la fonction. Il est possible d'appeler une fonction dont l'adresse est contenue dans un pointeur de fonction avec l'opérateur d'indirection *.

Pour déclarer un pointeur de fonction, il suffit de considérer les fonctions comme des variables. Leur déclaration est identique à celle des tableaux, en remplaçant les crochets par des parenthèses :

type (*identificateur)(paramètres);

où type est le type de la valeur renvoyée par la fonction, identificateur est le nom du pointeur de la fonction et paramètres est la liste des types des variables que la fonction attend comme paramètres, séparés par des virgules.

Exemple 4-14. Déclaration de pointeur de fonction[modifier | modifier le wikicode]

int (*pf)(int, int);   /* Déclare un pointeur de fonction. */

pf est un pointeur de fonction attendant comme paramètres deux entiers et renvoyant un entier.

Il est possible d'utiliser typedef pour créer un alias du type pointeur de fonction :

typedef int (*PtrFonct)(int, int);
PtrFonct pf;

PtrFonct est le type des pointeurs de fonctions.

Si pf est une fonction répondant à ces critères, on peut alors initialiser pf avec l'adresse de pf. De même, on peut appeler la fonction pointée par pf avec l'opérateur d'indirection.

Exemple 4-15. Déréférencement de pointeur de fonction[modifier | modifier le wikicode]

#include <stdio.h>    /* Autorise l'emploi de scanf et de printf. */

int f(int i, int j)   /* Définit une fonction. */
{
    return i+j;
}

int (*pf)(int, int);  /* Déclare un pointeur de fonction. */

int main(void)
{
    int l, m;         /* Déclare deux entiers. */
    pf = &f;          /* Initialise pf avec l'adresse de la fonction f. */
    printf("Entrez le premier entier : ");
    scanf("%u",&l);   /* Initialise les deux entiers. */
    printf("\nEntrez le deuxième entier : ");
    scanf("%u",&m);

/* Utilise le pointeur pf pour appeler la fonction f
   et affiche le résultat : */

    printf("\nLeur somme est de : %u\n", (*pf)(l,m));
    return 0;
}

L'intérêt des pointeurs de fonction est de permettre l'appel d'une fonction parmi un éventail de fonctions au choix.

Par exemple, il est possible de faire un tableau de pointeurs de fonctions et d'appeler la fonction dont on connaît l'indice de son pointeur dans le tableau.

Exemple 4-16. Application des pointeurs de fonctions[modifier | modifier le wikicode]

#include <stdio.h>  /* Autorise l'emploi de scanf et de printf. */

/* Définit plusieurs fonctions travaillant sur des entiers : */

int somme(int i, int j)
{
    return i+j;
}

int multiplication(int i, int j)
{
    return i*j;
}

int quotient(int i, int j)
{
    return i/j;
}

int modulo(int i, int j)
{
    return i%j;
}

typedef int (*fptr)(int, int);
fptr ftab[4];

int main(void)
{
    int i,j,n;
    ftab[0]=&somme;          /* Initialise le tableau de pointeur */
    ftab[1]=&multiplication; /* de fonctions. */
    ftab[2]=&quotient;
    ftab[3]=&modulo;
    printf("Entrez le premier entier : ");
    scanf("%u",&i);          /* Demande les deux entiers i et j. */
    printf("\nEntrez le deuxième entier : ");
    scanf("%u",&j);
    printf("\nEntrez la fonction : ");
    scanf("%u",&n);          /* Demande la fonction à appeler. */
    if (n < 4 && n >= 0)
        printf("\nRésultat : %u.\n", (*(ftab[n]))(i,j) );
    else
        printf("\nMauvais numéro de fonction.\n");
    return 0;
}

Références de fonctions[modifier | modifier le wikicode]

Les références de fonctions sont acceptées en C++. Cependant, leur usage est assez limité. Elles permettent parfois de simplifier les écritures dans les manipulations de pointeurs de fonctions. Mais comme il n'est pas possible de définir des tableaux de références, le programme d'exemple donné ci-dessus ne peut pas être récrit avec des références.

Les références de fonctions peuvent malgré tout être utilisées à profit dans le passage des fonctions en paramètre dans une autre fonction. Par exemple :

#include <stdio.h>  // Autorise l'emploi de scanf et de printf.

// Fonction de comparaison de deux entiers :

int compare(int i, int j)
{
    if (i<j) return -1;
    else if (i>j) return 1;
    else return 0;
}

// Fonction utilisant une fonction en tant que paramètre :

void trie(int tableau[], int taille, int (&fcomp)(int, int))
{
    // Effectue le tri de tableau avec la fonction fcomp.
    // Cette fonction peut être appelée comme toutes les autres
    // fonctions :
    printf("%d", fcomp(2,3));
      &vellip;
    return ;
}

int main(void)
{
    int t[3]={1,5,2};
    trie(t, 3, compare);   // Passage de compare() en paramètre.
    return 0;
}

Mélange de références et pointeurs de fonction et syntaxe ambiguë[modifier | modifier le wikicode]

En fait avec la majorité des compilateurs, dont GCC et Visual C++ il est possible d'utiliser un certain nombre de syntaxes plus ou moins élégantes; exploitant l’ambiguïté du type pointeur de fonction :

void Func()
{
    printf("appel de Func\n");
}

void Func2()
{
    printf("appel de Func2\n");
}

void F(void f1(),void f2())
{
    /// avec GCC f1 et f2 se comportent comme des références constantes
    /// avec visual C++ f1 et f2 se comportent comme des références non constantes
    /// sur pointeur de fonction
    //f1 = f2; // erreur de syntaxe avec GCC et Visual C++
    
    // l'échange des références/pointeurs fonctionne avec GCC 
    // mais buggent à l'exécution avec Visual C++ (car références non constantes)
    void **a = (void**)&f1; void **b = (void**)&f2; *a = *b;
    f1();
    (*f1)();
}

void G(void (*f1)(),void (*f2)())
{
    /// les pointeurs de fonctions se comportent comme prévu
    f1 = f2;
    f1();
    (*****f1)(); /// l'étoile ne fait rien sur les pointeurs de fonction
}

void H(void (&f1)(),void (&f2)())
{
    /// impossible d'échanger deux références non constantes
    /// car le compilateur donnera l'adresse de l'objet pointé
    /// au lieu de l'adresse de la référence
    f1();
     (*f1)();
}

/// références constantes sur pointeur de fonction ...
void I(void (* const & f1)(),void (* const & f2)())
{
    //f1 = f2; // erreur de syntaxe
    /// les références constantes sont comme des pointeurs constants
    /// qui se déréférenceraient automatiquement
    /// on peut donc les échanger avec un cast de leur adresse en (void**)
    void **a = (void**)&f1; void **b = (void**)&f2; *a = *b;
    f1();
    (*f1)();
}

int main()
{
    void (*ptr1)() = Func; void (*ptr2)() = Func;
    void (&ref1)() = Func; void (&ref2)() = Func;

    F(Func,Func2);
    F(&Func,&Func2);
    F(ptr1,ptr2);
    F(ref1,ref2);

    G(Func,Func2);
    G(&Func,&Func2);
    G(ptr1,ptr2);
    G(ref1,ref2);

    H(Func,Func2);
    //H(&Func,&Func2); erreur de syntaxe logique car référence non constante
    //H(ptr1,ptr2); erreur de syntaxe beaucoup moins logique
    H(ref1,ref2);

    I(Func,Func2);
    I(&Func,&Func2);
    I(ptr1,ptr2);
    I(ref1,ref2);

    return 0;
}