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 :

Pointeur générique (void *)


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    80
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 80
    Par défaut Pointeur générique (void *)
    Bonjour,

    Soit le code ci-dessous :
    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
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #include <stdio.h>
     
    void *essai1(void *p)
    {
      void **q = (void **)p;
      return (*q);
    }
     
    void *essai2(void *p)
    {
      double **q = (double **)p;
      return (*q);
    }
     
    int main(void)
    {
      {/* 1er exemple */
        int x = 4;
        int *a = &x;
     
        printf("a=%p\n", (void *)a);
        printf("*p=%p\n", essai1(&a));
        printf("*p=%p\n\n", essai2(&a));
      }
      {/* 2eme exemple */
        char *a = "abCd";
     
        printf("a=%p\n", a);
        printf("*p=%p\n", essai1(&a));
        printf("*p=%p\n\n", essai2(&a));
      }
      return 0;
    }
    [Dans essai2, j'ai mis le type double comme j'aurais pu mettre n'importe quel autre type.] Pour chaque exemple, les adresses qui s'affichent sont identiques. Ma question est : les fonctions essai1 et essai2 ou tout autre fonction similaire me renverront-elles toujours a, quel que le type de l'objet vers lequel a pointe ?

    Merci,

    Candide

  2. #2
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par c-candide
    Ma question est : les fonctions essai1 et essai2 ou tout autre fonction similaire me renverront-elles toujours a, quel que le type de l'objet vers lequel a pointe ?
    Oui. Ton code est inutilement compliqué et il manque des choses ...
    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
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
    #include <stdio.h>
     
    void *essai1 (void *p)
    {
       void **q = p;
       return *q;
    }
     
    void *essai2 (void *p)
    {
       double **q = p;
       return *q;
    }
     
    int main (void)
    {
       {                            /* 1er exemple */
          int x = 4;
          int *a = &x;
     
          printf ("a=%p\n", (void *) a);
          printf ("*p=%p\n", essai1 (&a));
          printf ("*p=%p\n\n", essai2 (&a));
       }
     
       {                            /* 2eme exemple */
          char const *a = "abCd";
     
          printf ("a=%p\n", (void *) a);
          printf ("*p=%p\n", essai1 (&a));
          printf ("*p=%p\n\n", essai2 (&a));
       }
       return 0;
    }

  3. #3
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    80
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 80
    Par défaut Encore une petite question svp
    Bonsoir Emmanuel et merci de ta réponse.

    Citation Envoyé par Emmanuel Delahaye
    Oui. Ton code est inutilement compliqué et il manque des choses ...
    Ah ! rien ne t'échappe ! En effet, j'avais crû que l'abscence du cast en void ** et double ** m'envoyait un warning.
    Pour les parenthèses inutiles après le return, OK. Je suppose que le manque auquel tu fais allusion est le const mais je ne vois pas en quoi c'est obligatoire, c'est des habitudes de bonne programmation, non ? ou j'ai manqué quelque chose d'important ?

    Sinon, encore une petite confirmation si possible. Je me lance dans l'écriture de programmes génériques et tout n'est pas encore bien clair. Par exemple, je dispose d'un tableau de pointeurs vers des "données" (de type et de taille inconnus a priori) et je passe ce tableau en argument à une fonction ayant en paramètre un pointeur sur void. Dans cette fonction, j'ai besoin de récupérer l'adresse des données (que je fais passer ensuite à une fonction-callback). D'où mon interrogation toute bête dans le programme tout bête suivant :
    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>
     
    void *essai(void *p)
    {
      char **char_p = p;
      return *(char_p + 1);
     }
     
    int main(void)
    {
      int a = 2007, b= 22;
      int *t[2];
     
      t[0] = &a;
      t[1] = &b;  
     
      printf("adresse de b = %p \n", (void *)t[1]);
      printf("adresse retournee %p\n", essai(t)); 
     
      return 0;
    }
    C'est bien normal et portable que les mêmes adresses s'affichent, n'est-ce pas ? J'utilise le type char dans ma fonction essai mais n'importe quel type donnerait la même réponse, est-ce bien ainsi ?

    Merci encore,

    Candide

  4. #4
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    Ben d'abord ta fonction est void * ...

    Il faudrait caster en void * ton retour..

    D'autre part ton arithmétique de pointeur (+1) ne marche qu'avec des char..

    Et enfin, pour faire ce genre de routine générique, ou tu maniupleras un tableau, moi je passerais plutot l'adresse d'une structure qui contient le tableau... Mais je n'ai peut être pas tous les atouts en main..

  5. #5
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par souviron34
    Ben d'abord ta fonction est void * ...

    Il faudrait caster en void * ton retour..
    Ben non, pas la peine. Le type void * est compatible avec tous les type pointeurs sur objet. La conversion est implicite.
    D'autre part ton arithmétique de pointeur (+1) ne marche qu'avec des char..
    Elle fonctionne avec tout les pointeurs typés. Ici, on a un pointeur sur char* (char **), le fonction est parfaitement défini et conforme.

  6. #6
    Expert confirmé
    Avatar de Thierry Chappuis
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Mai 2005
    Messages
    3 499
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Industrie Pharmaceutique

    Informations forums :
    Inscription : Mai 2005
    Messages : 3 499
    Par défaut
    Citation Envoyé par c-candide
    Je suppose que le manque auquel tu fais allusion est le const mais je ne vois pas en quoi c'est obligatoire, c'est des habitudes de bonne programmation, non ? ou j'ai manqué quelque chose d'important ?
    Une constante chaîne de caractère n'étant pas modifiable, il est normal de pointer dessus avec une variable de type pointeur sur char constant. gcc avec l'option -Wwrite-strings donne un avertissement lorsque le qualificatif const n'est pas utilisé.

    Citation Envoyé par c-candide
    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>
     
    void *essai(void *p)
    {
      char **char_p = p;
      return *(char_p + 1);
     }
     
    int main(void)
    {
      int a = 2007, b= 22;
      int *t[2];
     
      t[0] = &a;
      t[1] = &b;  
     
      printf("adresse de b = %p \n", (void *)t[1]);
      printf("adresse retournee %p\n", essai(t)); 
     
      return 0;
    }
    C'est bien normal et portable que les mêmes adresses s'affichent, n'est-ce pas ? J'utilise le type char dans ma fonction essai mais n'importe quel type donnerait la même réponse, est-ce bien ainsi ?
    C'est normal que la même adresse s'affiche dans les deux cas. Maintenant, je ne vois pas pourquoi tu déclares char_p comme une variable de type char **. t[1] est de type pointeur sur int et est équivalent à *(t + 1). Et *(char_p + 1) et char_p[1] sont équivalents. Comme char_p est initialisé avec la valeur de t et que tous deux pointent sur un pointeur, (t + 1) ou (char_p + 1) référencent la même adresse. Attention tout de même: se jouer du système de typage peut réserver des surprises...

    Thierry
    "The most important thing in the kitchen is the waste paper basket and it needs to be centrally located.", Donald Knuth
    "If the only tool you have is a hammer, every problem looks like a nail.", probably Abraham Maslow

    FAQ-Python FAQ-C FAQ-C++

    +

  7. #7
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    80
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 80
    Par défaut
    Citation Envoyé par mujigka
    Une constante chaîne de caractère n'étant pas modifiable, il est normal de pointer dessus avec une variable de type pointeur sur char constant. gcc avec l'option -Wwrite-strings donne un avertissement lorsque le qualificatif const n'est pas utilisé.
    Normal, ça veut dire quoi ? En tous cas, ça n'a rien d'obligatoire, si j'ai bien compris on utilise const essentiellement pour des raisons de sécurité et de commentaire du code. En particulier dans un mini-exemple, il me paraît excessif de dire que "ça manque".

    C'est normal que la même adresse s'affiche dans les deux cas.
    Merci de cette confirmation.

    Maintenant, je ne vois pas pourquoi tu déclares char_p
    comme une variable de type char **
    tu veux dire "comme une variable de type char **" plutôt "comme une variable de type machin **" ? Pas de raison particulière et c'est justement le sens de la deuxième quastion que je posais.


    Attention tout de même: se jouer du système de typage peut réserver des surprises...
    Oui, j'ai entendu dire que ça pouvait être dangereux, les casts peuvent être dangereux puisqu'ils font taire le compilateur mais tout ceci reste assez vague pour moi, si quelqu'un pouvait développer ou mieux, donner un court exemple.

    Mais il me semble que dans certaines circonstances, on ne peut se passer de "se jouer du système de typage", en particulier quand on écrit du code générique, non ?

    Candide

  8. #8
    Expert confirmé
    Avatar de Thierry Chappuis
    Homme Profil pro
    Enseignant Chercheur
    Inscrit en
    Mai 2005
    Messages
    3 499
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : Suisse

    Informations professionnelles :
    Activité : Enseignant Chercheur
    Secteur : Industrie Pharmaceutique

    Informations forums :
    Inscription : Mai 2005
    Messages : 3 499
    Par défaut
    Citation Envoyé par c-candide
    Normal, ça veut dire quoi ? En tous cas, ça n'a rien d'obligatoire, si j'ai bien compris on utilise const essentiellement pour des raisons de sécurité et de commentaire du code. En particulier dans un mini-exemple, il me paraît excessif de dire que "ça manque".
    Normal n'est pas le terme approprié, j'aurais du mettre naturel. Maintenant, tu as raison, la norme du langage ne l'impose pas. C'est toutefois une bonne habitude à prendre, même dans un mini-exemple. Comme je le perçoit, c'était juste un conseil qui t'a été donné, et non un reproche...

    Citation Envoyé par c-candide
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Maintenant, je ne vois pas pourquoi tu déclares char_p 
    comme une variable de type char **
    tu veux dire "comme une variable de type char **" plutôt "comme une variable de type machin **" ?
    Via ta fonction essai(), c'est à quelques détails prêts, c'est comme si tu écrivais.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    int a = 2007;
    int b= 22;
    int *t[2] = {&a, &b};
    char **char_p = (void *) t;
    Au final, comme tu l'as fait remarquer, *char_p et *t contiennent la même adresse. t s'attend à trouver un entier de type int à cette adresse, tandis que char_p s'attend à trouver un caractère.

    Citation Envoyé par c-candide
    Mais il me semble que dans certaines circonstances, on ne peut se passer de "se jouer du système de typage", en particulier quand on écrit du code générique, non ?
    Oui, je n'ai pas dit le contraire, mais c'est délicat, et il faut savoir ce qui l'on fait (et c'est pas toujours très propre). Pour tout savoir au sujet de la programmation générique en C, un excellent tutoriel est celui de Romuald Perrot http://rperrot.developpez.com/articles/c/genericite/ .

    Thierry
    "The most important thing in the kitchen is the waste paper basket and it needs to be centrally located.", Donald Knuth
    "If the only tool you have is a hammer, every problem looks like a nail.", probably Abraham Maslow

    FAQ-Python FAQ-C FAQ-C++

    +

  9. #9
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par c-candide
    Je suppose que le manque auquel tu fais allusion est le const mais je ne vois pas en quoi c'est obligatoire, c'est des habitudes de bonne programmation, non ? ou j'ai manqué quelque chose d'important ?
    Oui. Il n'est pas garanti qu'une chaine soit modifiable. Il est donc conseillé de le préciser avec const. Une option de gcc permet de le vérifier.

    http://emmanuel-delahaye.developpez....tm#cfg_compilo
    Sinon, encore une petite confirmation si possible. Je me lance dans l'écriture de programmes génériques et tout n'est pas encore bien clair. Par exemple, je dispose d'un tableau de pointeurs vers des "données" (de type et de taille inconnus a priori) et je passe ce tableau en argument à une fonction ayant en paramètre un pointeur sur void.
    Si c'est un tableau générique, il va manquer des informations. Regarde le prototype de qsort() et tu vas comprendre.

  10. #10
    Membre éclairé
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    80
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2006
    Messages : 80
    Par défaut
    Citation Envoyé par Emmanuel Delahaye
    Si c'est un tableau générique, il va manquer des informations. Regarde le prototype de qsort() et tu vas comprendre.
    Tu veux dire la taille unitaire des éléments ou leur nombre ? Bien sûr. Avant de me lancer dans de la programmation générique, j'ai étudié en détail l'implémentation de qsort ou de bsearch par Plauger et je pense avoir compris comment ça fonctionne. Mais pour ce que je veux faire, il me semble que j'ai besoin d'un niveau de déréférencement de plus (d'où mes questions avec des void ** ou machin **). Je m'explique :

    Je veux faire un module de traitement de tableaux génériques et qui incorpore des opérations génériques entre tableaux.

    Par exemple, le module doit être capable de faire des sommes de tableaux au sens évident du terme, par exemple la somme du tableau d'entiers {2, 5} et du tableau {6, 1} va donner le tableau {8, 6}.

    Mais je veux que cette addition soit très générique parce que je voudrais qu'elle marche non seulement pour des entiers mais pour des doubles, des types que j'aurais définis comme un type fraction (au sens usuel du terme) avec une définition de préférence générique d'une addition de fractions. Et je me suis rendu compte que les arguments de ma fonction de somme générique devaient être non par les données (ci-dessus, les données étaient des int ou encore les fractions) mais des pointeurs vers les données tout simplement parce que la fonction de somme générique de tableaux ne peut pas savoir de quelle quantité de mémoire le résultat de la somme va nécessiter, seule une fonction ad hoc travaillant directement avec les données peut donner cette quantité de mémoire. La taille du résultat peut donc être quelconque, par exemple imaginons qu'on définisse l'addition de deux chaînes comme leur concaténation (ie "bla" plus "blabla" donne "blablabla"). Je peux alors définir la somme de deux tableaux de chaînes de manière naturelle, par exemple,
    {"azert", "bla"} plus {"y", "blabla"} serait {"azerty", "blablabla"} donc il va falloir allouer pour le résultat, ce n'est pas ma fonction de somme générique de tableaux qui pourra le faire mais ma fonction de concaténation. Voilà, je ne sais pas si je suis parvenu à expliquer clairement mon problème. Je suis intéressé par tout avis.

    Merci

    Candide

  11. #11
    Expert confirmé

    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    10 610
    Détails du profil
    Informations personnelles :
    Âge : 67
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 610
    Billets dans le blog
    2
    Par défaut
    si ça vous intéresse, j'ai un petit module qui lit un fichier de ressources comme on en trouve dans X/Motif :

    fichier de ressources :

    des lignes de texte contenant des chaînes, des caractères, des entiers, des réels, des booléens,

    module :

    lit le fichier
    stocke chaque élément dans une structure avec le bon type..

    Mais aussi

    assigne une valeur par défaut à chaque élément de la structure..

    Exemple de fonction (juste le début) :

    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
     
    static int Init_Resource ( char *param_type, 
    	                      void *address, void *value )
    {
    char   **caddress = (char **)address, *cvalue = (char *)value ;
    int     *iaddress = (int *)address, *ivalue = (int *)value ;
    float   *faddress = (float *)address, *fvalue = (float *)value ;
    double  *daddress = (double *)address, *dvalue = (double *)value ;
    Boolean *baddress = (Boolean *)address, *bvalue = (Boolean *)value ;
     
       if ( strcmp ( param_type, "char" ) == 0 )
         {
         }
       else
         {
         }
    }
    Exemple d'appel :

    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
     
       if ( MCore_DefineResourceElement ( "MCore", "appliDocument", "char",
    				      &(Config->appli_document), "" ) == ERROR )
          return ERROR;
     
       i = 0; /* will get detected from dbName using /etc/services */
       if ( MCore_DefineResourceElement ( "MCore", "dbPort", "int", 
    				      &(Config->port), &i ) == ERROR )
     
       f = 6378137.0 ;
       if ( MCore_DefineResourceElement ( "MCore", "datum_SemiMajorAxis", "double",
    				      &(Config->datum_semi_majoraxis), &f ) == ERROR )
          return ERROR;
     
     
       F = False ;
       if ( MCore_DefineResourceElement ( "MCore", "allowBadColorBehaviour", "Boolean", 
    				      &(Config->allow_bad_color_behaviour), &F ) == ERROR )
          return ERROR;

  12. #12
    Expert éminent
    Avatar de Emmanuel Delahaye
    Profil pro
    Retraité
    Inscrit en
    Décembre 2003
    Messages
    14 512
    Détails du profil
    Informations personnelles :
    Âge : 68
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2003
    Messages : 14 512
    Par défaut
    Citation Envoyé par c-candide
    C'est bien normal et portable que les mêmes adresses s'affichent, n'est-ce pas ? J'utilise le type char dans ma fonction essai mais n'importe quel type donnerait la même réponse, est-ce bien ainsi ?
    Oui. La taille des pointeurs est la même quelque soit le type.

  13. #13
    Membre averti
    Inscrit en
    Février 2007
    Messages
    14
    Détails du profil
    Informations forums :
    Inscription : Février 2007
    Messages : 14
    Par défaut
    les notions utiliées ds votre code:
    -->Pointeur de Pointeur
    -->Conversion de type de pointeur( void * => double * ,...):
    ---->Explicite et implicite

    mais vs remarquez que vs tombez sur la meme valeur adressée.
    une remarque pr cette conversion implicite il est possible :
    ...
    int *a = &x;
    ....
    char *a = "abCd";
    ...

    vous pouvez essayer ce code ca donne le meme chose alors porqoui le pointeur en double (Ptr de Ptr :: **)
    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
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
    #include <stdio.h>
     
    void *essai1(void *p)
    {
      void *q = p;
      return (q);
    }
     
    void *essai2(void *p)
    {
      double *q = (double *)p;
      return (q);
    }
     
    int main(void)
    {
      {/* 1er exemple */
        int x = 4;
        int *a = &x;
     
        printf("a=%p\n", (void *)a);
        printf("*p=%p\n", essai1(&a));
        printf("*p=%p\n\n", essai2(&a));
      }
      {/* 2eme exemple */
        char *a = "abCd";
     
        printf("a=%p\n", a);
        printf("*p=%p\n", essai1(&a));
        printf("*p=%p\n\n", essai2(&a));
      }
       getchar();
      return 0;
    }

Discussions similaires

  1. portabilité du pointeur générique void*
    Par uknow dans le forum C
    Réponses: 6
    Dernier message: 09/01/2011, 15h48
  2. Pointeur générique vers d'autres types d'objets
    Par LapinGarou dans le forum MFC
    Réponses: 11
    Dernier message: 15/09/2006, 16h48
  3. déréférencer un pointeur générique ?
    Par tintin72 dans le forum C
    Réponses: 6
    Dernier message: 23/07/2006, 12h40
  4. Question sur les pointeurs génériques
    Par mikedavem dans le forum C
    Réponses: 16
    Dernier message: 24/05/2006, 11h56
  5. pointeur générique
    Par ghostdog dans le forum C
    Réponses: 14
    Dernier message: 09/11/2005, 15h23

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