IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

problème avec les pointeurs de fonctions


Sujet :

C

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 24
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2017
    Messages : 15
    Points : 12
    Points
    12
    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
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    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
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 24
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2017
    Messages : 15
    Points : 12
    Points
    12
    Par défaut
    Ah ok tout simplement c'est impossible. Merci !

  4. #4
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 823
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 823
    Points : 7 119
    Points
    7 119
    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
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2017
    Messages
    15
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 24
    Localisation : France, Dordogne (Aquitaine)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2017
    Messages : 15
    Points : 12
    Points
    12
    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
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

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

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    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
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    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
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 371
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 371
    Points : 23 626
    Points
    23 626
    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
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 471
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 471
    Points : 6 110
    Points
    6 110
    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
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    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