1. #1
    Futur Membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    avril 2017
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 18
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : avril 2017
    Messages : 11
    Points : 8
    Points
    8

    Par défaut problème avec les pointeurs de fonctions

    Bonjour,
    j'ai aujourd'hui une nouvelle question ! Alors voilà, je me suis lancé dans la compréhension des pointeurs de fonctions. Sauf que dès mon premier essai j'ai une erreur. Voici le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include "my.h"
     
    int main(int argc, char **argv)
    {
      int item;
      int (*applyf)(void *);
      item = 1;
      applyf = &my_put_nbr;
      applyf(&item);
    }
    (avec la fonction my_put_nbr qui est déclaré comme ceci : int my_put_nbr(int);)

    j'ai beau essayer (*applyf) = &my_put_nbr;,
    (*applyf) = my_put_nbr;,
    applyf = my_put_nbr;, rien ne fonctionne.
    J'ai l'erreur suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    test.c:8:10: warning: assignement from incompatible pointer type
           applyf = &my_put_nbr;
                  ^
    ou bien :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    test.c:8:13: error 1value required as left operand of assignment
           (*applyf) = my_put_nbr;
                     ^
    Je ne comprends pas à quoi ces erreurs correspondent...
    Je sais que si je déclare mon pointeur int (*applyf)(int) ça fonctionne, mais d'après ce que j'ai lu sur le net, les pointeurs void*, peuvent pointer n'importe quel type de variable, donc j'aurai voulu la déclarer avec int (*applyf)(void *), ce qui m'aurait permis de ne pas simplement utilisé des fonctions qui ont des int en paramètres.

  2. #2
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    septembre 2005
    Messages
    26 431
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2005
    Messages : 26 431
    Points : 38 045
    Points
    38 045

    Par défaut

    Le pointeur de fonction et la fonction pointée doivent avoir exactement le même prototype.

    Le cas contraire n'a pas de sens de toute façon, car il n'y a pas moyen d'appeler "dynamiquement" un pointeur de fonction: Le nombre et les types des paramètres est forcément fixe lors de l'appel!
    Ce qui veut dire que tu passeras forcément "un int" à ton pointeur de fonction, tu n'as donc pas besoin de pouvoir pointer sur des fonctions qui acceptent autre chose.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  3. #3
    Futur Membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    avril 2017
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 18
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : avril 2017
    Messages : 11
    Points : 8
    Points
    8

    Par défaut

    Ah ok tout simplement c'est impossible. Merci !

  4. #4
    Membre expert
    Avatar de fred1599
    Homme Profil pro
    Enseignant
    Inscrit en
    juillet 2006
    Messages
    2 167
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : juillet 2006
    Messages : 2 167
    Points : 3 471
    Points
    3 471

    Par défaut

    Ah ok tout simplement c'est impossible. Merci !
    Ce n'est pas ce qu'il a dit ! Ta façon de faire n'est pas correcte tout simplement... relis ce qu'il a écrit.
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  5. #5
    Futur Membre du Club
    Homme Profil pro
    Lycéen
    Inscrit en
    avril 2017
    Messages
    11
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 18
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Lycéen

    Informations forums :
    Inscription : avril 2017
    Messages : 11
    Points : 8
    Points
    8

    Par défaut

    Citation Envoyé par Médinoc Voir le message
    Le pointeur de fonction et la fonction pointée doivent avoir exactement le même prototype.
    Donc appelé une fonction avec un int en paramètre avec un pointeur de fonction avec un void* en paramètre est bien impossible non..?

  6. #6
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    septembre 2005
    Messages
    26 431
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2005
    Messages : 26 431
    Points : 38 045
    Points
    38 045

    Par défaut

    Oui. Et si tu essaies quand même, c'est la recette du désastre.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Membre expert
    Inscrit en
    mars 2005
    Messages
    1 026
    Détails du profil
    Informations forums :
    Inscription : mars 2005
    Messages : 1 026
    Points : 3 023
    Points
    3 023

    Par défaut

    Citation Envoyé par Deezio Voir le message
    d'après ce que j'ai lu sur le net, les pointeurs void*, peuvent pointer n'importe quel type de variable
    Attention : la norme ne garantit pas qu'un pointeur void * et un pointeur de fonction fassent la même taille. Voici ce que l'on peut affirmer :

    • un pointeur de type void * peut stocker l'adresse de n'importe quel type de données ;
    • un pointeur de fonction peut stocker l'adresse d'une fonction de n'importe quel type ;
    • un pointeur void * ne peut pas stocker l'adresse d'une fonction ;
    • un pointeur de fonction ne peut pas stocker l'adresse d'un champ de données.


    Illustration :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <stdio.h>
     
    int inc(int i) { return i + 1; }
     
    int main() {
        int i = 42;
     
        void *p = &i; // bien
        printf("%d\n", *((int *)p));
     
        void (*q)(void) = (void (*)())inc; // bien
        printf("%d\n", ((int(*)(int)) q)(i));
     
        void (*r)(void) = (void(*)())&i; // pas bien : UB!
        printf("%d\n", *((int *)r));
     
        void *s = inc; // pas bien : UB!
        printf("%d\n", ((int(*)(int)) s)(i));
     
        return 0;
    }

    Citation Envoyé par Deezio Voir le message
    Donc appelé une fonction avec un int en paramètre avec un pointeur de fonction avec un void* en paramètre est bien impossible non..?
    L'appel tel quel, oui. Mais caster la valeur du pointeur vers le type d'origine de la fonction avant l'appel est autorisé et déterminé (cf. exemple précédent).

  8. #8
    Responsable Modération
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    septembre 2007
    Messages
    6 847
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : septembre 2007
    Messages : 6 847
    Points : 21 344
    Points
    21 344

    Par défaut

    Bonjour,

    Citation Envoyé par Deezio Voir le message
    Je sais que si je déclare mon pointeur int (*applyf)(int) ça fonctionne, mais d'après ce que j'ai lu sur le net, les pointeurs void*, peuvent pointer n'importe quel type de variable, donc j'aurai voulu la déclarer avec int (*applyf)(void *), ce qui m'aurait permis de ne pas simplement utilisé des fonctions qui ont des int en paramètres.
    Attention à ne pas s'emmeler les pinceaux à ce stade en particulier (car je pense que la confusion vient de là) : ton « pointeur void » a tout-à-fait le droit de pointer un entier int quelque part en mémoire (fût-ce directement l'instance d'une variable). Il reste que c'est bien le pointeur void lui-même que tu dois passer à ta fonction, et pas l'entier directement. Donc les prototypes int (*) (void *) et int (*) (int) sont bien incompatibles entre eux (ne serait-ce que parce que tu ne pourrais pas déréférencer ton entier à l'aide de * ou ->).

    In situ dans ton programme C, cela donnerait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        int (* applyf) (void *);
        int x;
     
        applyf (&x); /* Avec & */
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
        int (* applyf) (int);
        int x;
     
        applyf (x); /* Sans & */
    C'est important car tout cela est vrai sur la forme (et le compilateur a donc raison de se plaindre) mais qu'en pratique, les prototypes de pointeurs de fonction en (*) (void *) se rencontrent fréquemment dans les fonctions de callback définies par les API des frameworks de plus haut niveau : quand tu mets en place un gestionnaire de signal avec sigaction() ou que tu associes une action quelconque avec un bouton d'une interface graphique, l'action en question est par nature très dépendante du contexte, ce qui interdit « en principe » la définition a priori d'une interface universelle. En réalité, le prototype concerné est généralement écrit pour admettre un (void *) qui, lui, peut pointer n'importe quoi, la fonction concernée reconnaissant ses petits et transtypant le pointeur pour référencer la plupart du temps une structure en mémoire.

    Maintenant, si l'information à transmettre n'est en fait que du message passing, voire un simple flag, alors il arrive fréquemment que l'on transtype directement l'entier en pointeur pour le transmettre, et réciproquement, d'où les remarques de Matt_Houston et Pyramidev. C'est plus simple à écrire et ça évite d'avoir à réserver inutilement de la mémoire (même dans la pile) et d'avoir à la libérer ensuite, surtout si ce n'est que pour quelques octets. MAIS cela reste une initiative du programmeur et pas une définition formelle du langage C.

  9. #9
    Membre chevronné
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    avril 2016
    Messages
    413
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : avril 2016
    Messages : 413
    Points : 1 804
    Points
    1 804

    Par défaut

    Citation Envoyé par Matt_Houston Voir le message
    Attention : la norme ne garantit pas qu'un pointeur void * et un pointeur de fonction fassent la même taille. Voici ce que l'on peut affirmer :

    • un pointeur de type void * peut stocker l'adresse de n'importe quel type de données ;
    • un pointeur de fonction peut stocker l'adresse d'une fonction de n'importe quel type ;
    • un pointeur void * ne peut pas stocker l'adresse d'une fonction ;
    • un pointeur de fonction ne peut pas stocker l'adresse d'un champ de données.
    Je confirme.

    ISO/IEC 9899:201x (draft de C11) :
    6.2.4 Storage durations of objects

    [...]

    A pointer type may be derived from a function type or an object type, called the
    referenced type.

    [...]

    6.3.2.3 Pointers
    1 A pointer to void may be converted to or from a pointer to any object type. A pointer to
    any object type may be converted to a pointer to void and back again; the result shall
    compare equal to the original pointer.

    [...]

    8 A pointer to a function of one type may be converted to a pointer to a function of another
    type and back again; the result shall compare equal to the original pointer. If a converted
    pointer is used to call a function whose type is not compatible with the referenced type,
    the behavior is undefined.
    Avec un cast en force, on peut stocker l'adresse d'une fonction dans n'importe quel type de pointeur de fonction.
    Par contre, au moment de l'appel, il faut que le pointeur de fonction soit du bon type.

  10. #10
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur systèmes embarqués
    Inscrit en
    juin 2009
    Messages
    3 505
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 30
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur systèmes embarqués

    Informations forums :
    Inscription : juin 2009
    Messages : 3 505
    Points : 9 241
    Points
    9 241
    Billets dans le blog
    1

    Par défaut

    Avec un cast en force, on peut stocker n'importe quoi dans n'importe quoi.
    Par contre, au moment de l'appel, il faut s'attendre à ce que ça fasse tout autant n'importe quoi.


Discussions similaires

  1. Petit problème avec les pointeurs et variable
    Par mitherkiller dans le forum C
    Réponses: 5
    Dernier message: 09/03/2007, 22h05
  2. problème avec les pointeurs en c
    Par dialloma dans le forum C
    Réponses: 14
    Dernier message: 01/01/2007, 21h22
  3. probléme avec les pointeurs
    Par killer_instinct dans le forum C++
    Réponses: 6
    Dernier message: 11/12/2006, 11h37
  4. Comment fait ça avec les pointeurs, la fonction et les struc
    Par mahdianis dans le forum Balisage (X)HTML et validation W3C
    Réponses: 3
    Dernier message: 24/02/2006, 18h01
  5. [TTreeView] Problème avec les pointeurs d'objet
    Par BlackWood dans le forum Composants VCL
    Réponses: 2
    Dernier message: 02/07/2004, 14h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo