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 :

Généricité void *


Sujet :

C

  1. #1
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 84
    Points : 69
    Points
    69
    Par défaut Généricité void *
    Bonsoir,

    Pourriez-vous m'expliquer pourquoi ce code compile :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void swap( void * d1 , void * d2 )
    {
    	void * tmp;
    	tmp = *(void **)d1;
    	*(void **)d1 = *(void **)d2;
    	*(void **)d2 = tmp;
    }
    Je ne comprend pas le fait que le compilateur accepte de déréférencer un pointeur void en passant par un double pointeur...

    Merci.

  2. #2
    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
    Bonsoir,

    Peut-être bien parce que tu castes et que caster ça veut dire "tais-toi je sais ce que je fais".

  3. #3
    Expert éminent sénior

    Avatar de fearyourself
    Homme Profil pro
    Ingénieur Informaticien Senior
    Inscrit en
    Décembre 2005
    Messages
    5 121
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Ingénieur Informaticien Senior
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2005
    Messages : 5 121
    Points : 11 877
    Points
    11 877
    Par défaut
    Et aussi que déréférencer un double pointeur void a un sens or le faire pour un simple pointeur void n'en a aucun

  4. #4
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par fearyourself Voir le message
    Et aussi que déréférencer un double pointeur void a un sens or le faire pour un simple pointeur void n'en a aucun
    Mais encore ? Peux-tu développer ?
    Le sujet m'intéresse, et j'ai du mal je l'avoue à voir le sens du déférencement d'un double pointeur void.

  5. #5
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 84
    Points : 69
    Points
    69
    Par défaut
    Et aussi que déréférencer un double pointeur void a un sens or le faire pour un simple pointeur void n'en a aucun
    Oui, mais pourquoi ? Quoi qu'il en soit à la fin le pointeur générique ne sait pas vers quel type de variable son contenu pointe, d'ou l'impossibilité de déréférencer le pointeur. En quoi passer par un double pointeur résout le problème ?

  6. #6
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 984
    Points
    30 984
    Billets dans le blog
    1
    Par défaut
    Bonjour
    Citation Envoyé par sbill Voir le message
    Oui, mais pourquoi ? Quoi qu'il en soit à la fin le pointeur générique ne sait pas vers quel type de variable son contenu pointe, d'ou l'impossibilité de déréférencer le pointeur. En quoi passer par un double pointeur
    résout le problème ?
    C'est vrai qu'aller voir le contenu d'un void* n'a pas de sens, on ne sait pas vers quoi on pointe effectivement. En revanche, aller voir le contenu d'un "void **" c'est tout à fait possible et ne pose aucun soucis. Ce qui est au bout d'un "void **", c'est un "void *" et ça c'est quelque chose de tout à fait connu puisque c'est une adresse !!! Et les adresses, tout le monde sait les manipuler...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  7. #7
    Expert confirmé
    Avatar de gerald3d
    Homme Profil pro
    Conducteur de train
    Inscrit en
    Février 2008
    Messages
    2 291
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Conducteur de train
    Secteur : Transports

    Informations forums :
    Inscription : Février 2008
    Messages : 2 291
    Points : 4 941
    Points
    4 941
    Billets dans le blog
    5
    Par défaut
    Citation Envoyé par sbill Voir le message
    Oui, mais pourquoi ? Quoi qu'il en soit à la fin le pointeur générique ne sait pas vers quel type de variable son contenu pointe, d'ou l'impossibilité de déréférencer le pointeur. En quoi passer par un double pointeur résout le problème ?
    Le fait de parler de "double pointeur" me fait penser que la notion d'adressage n'est pas encore tout à fait claire pour toi.

  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 sbill Voir le message
    Oui, mais pourquoi ? Quoi qu'il en soit à la fin le pointeur générique ne sait pas vers quel type de variable son contenu pointe, d'ou l'impossibilité de déréférencer le pointeur. En quoi passer par un double pointeur résout le problème ?
    Parce qu'un « double pointeur void » est en réalité « un pointeur vers un pointeur vers void ». Quand tu le déréférences une fois, tu tombes sur le pointeur suivant qui, lui, a toujours (en pratique) le même format, même s'il pointe quelque chose d'indéfini.

    Par ailleurs, les pointeurs « void * » passés à ta fonction sont transmis depuis une autre fonction qui lui passe probablement l'adresse d'objets existants, donc même si c'est incorrect, tu éviteras probablement la segfault et ne t'apercevra pas de l'erreur sans avoir le nez dessus. Ta fonction a donc l'impression de permuter des pointeurs, ce qui lui suffit même si ce n'est pas le cas en réalité. S'il se trouve que les données pointées ont le même format que les pointeurs de ta machine cible, ce sera complètement transparent et le bug risque de rester latent un bon bout de temps. Si en revanche, elles ont des longueurs différentes, spécialement si elles sont plus courtes, alors la permutation va entraîner avec elle un morceau des variables à côté, voire un fragment du cadre de pile, et tu risques d'être confronté à un comportement très étrange qui sera difficile à localiser : le plantage risque de se produire a posteriori, dans une partie du code qui n'a rien à voir avec celle réellement incriminée.

  9. #9
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 984
    Points
    30 984
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Ta fonction a donc l'impression de permuter des pointeurs, ce qui lui suffit même si ce n'est pas le cas en réalité.
    Moi aussi j'ai pensé que ça permutait les adresses des variables. Un simple affichage des adresses avant et après m'a montré que ce n'était pas le cas

    Citation Envoyé par Obsidian Voir le message
    S'il se trouve que les données pointées ont le même format que les pointeurs de ta machine cible, ce sera complètement transparent et le bug risque de rester latent un bon bout de temps. Si en revanche, elles ont des longueurs différentes, spécialement si elles sont plus courtes, alors la permutation va entraîner avec elle un morceau des variables à côté, voire un fragment du cadre de pile, et tu risques d'être confronté à un comportement très étrange qui sera difficile à localiser
    Dans mes premiers tests, à base de int, ça fonctionnait. Mais quand j'ai voulu permuter des trucs divers et variés et un peu plus complexe c'est complètement parti en torche...

    Code c : 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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    #include <stdio.h>
     
    void swap(void * d1 , void * d2)
    {
    	void * tmp;
    	tmp = *(void **)d1;
    	*(void **)d1 = *(void **)d2;
    	*(void **)d2 = tmp;
    }
     
    void essai1()
    {
    	int x=5;
    	int y=0;
     
    	printf("x=%d, y=%d, &x=%p, &y=%p\n", x, y, &x, &y);
    	swap(&x, &y);
    	printf("x=%d, y=%d, &x=%p, &y=%p\n", x, y, &x, &y);
    }
     
    void essai2()
    {
    	char x='a';
    	char y='z';
     
    	printf("x=%c, y=%c, &x=%p, &y=%p\n", x, y, &x, &y);
    	swap(&x, &y);
    	printf("x=%c, y=%c, &x=%p, &y=%p\n", x, y, &x, &y);
    }
     
    void essai3()
    {
    	typedef struct {
    		char *txt;
    		int i;
    		double d;
    	} t_xxx;
    	t_xxx x={"toto", 5, 3.1416};
    	t_xxx y={"titi", 7, 2.71828};
     
    	printf("x=%s,%d,%f, y=%s,%d,%f, &x=%p, &y=%p\n", x.txt, x.i, x.d, y.txt, y.i, y.d, &x, &y);
    	swap(&x, &y);
    	printf("x=%s,%d,%f, y=%s,%d,%f, &x=%p, &y=%p\n", x.txt, x.i, x.d, y.txt, y.i, y.d, &x, &y);
    }
     
    int main()
    {
    	essai1();
    	essai2();
    	essai3();
    }

    Résultat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ make essai
    cc     essai.c   -o essai
    $ ./essai
    x=5, y=0, &x=0x7fff61b929ac, &y=0x7fff61b929a8
    x=1639524800, y=5, &x=0x7fff61b929ac, &y=0x7fff61b929a8
     
    x=a, y=z, &x=0x7fff61b929af, &y=0x7fff61b929ae
    x=, y=a, &x=0x7fff61b929af, &y=0x7fff61b929ae
     
    x=toto,5,3.141600, y=titi,7,2.718280, &x=0x7fff61b92990, &y=0x7fff61b92970
    x=titi,5,3.141600, y=toto,7,2.718280, &x=0x7fff61b92990, &y=0x7fff61b92970
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  10. #10
    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
    Hello,

    Citation Envoyé par Sve@r Voir le message
    Moi aussi j'ai pensé que ça permutait les adresses des variables. Un simple affichage des adresses avant et après m'a montré que ce n'était pas le cas
    Pas les adresses des variables de la fonction appelante, qui sont de toutes façons définies à la compilation et ne peuvent donc pas varier au cours du programme. Mais les adresses de ces variables sont passés en tant que pointeur à la fonction swap qui, elle, par le bias des casts, finit par penser que ce sont des pointeurs « void * » qui se trouvent à ces emplacement, ce qui n'est pas forcément le cas.

    Dans mes premiers tests, à base de int, ça fonctionnait. Mais quand j'ai voulu permuter des trucs divers et variés et un peu plus complexe c'est complètement parti en torche...
    Tout-à-fait. J'imagine que tu compiles sur une plateforme 64 bits, sur laquelle les pointeurs occupent 8 octets au lieu de quatre, ce qui pourrit l'essai 1. Chose encore plus flagrante avec l'essai 2 où ce sont deux caractères consécutifs que l'on manipule. Même avec du padding, il est probable que celui aligne les données sur des adresses multiples de 4 plutôt que 8.

    Par contre, dans le troisième essai, tu déplaces des structures (plus longues que 4 ou 8 en principe) mais qui commencent par un pointeur sur une chaîne ! Ces pointeurs ont exactement la même longueur qu'un « void * » et se retrouvent proprement intervertis. Tes structures voient donc leurs chaînes permutées alors que le reste des données a conservé sa place.

  11. #11
    Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2014
    Messages
    84
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2014
    Messages : 84
    Points : 69
    Points
    69
    Par défaut
    C'est compris ! merci

  12. #12
    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
    D'un autre côté, j'imagine bien un truc à base de macro pour faire un tel swap (mais ça aurait l'inconvénient d'évaluer chaque argument deux fois):
    Code C : 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
    #define swap(x, y) realswap((x), (y), sizeof (x), sizeof (y))
     
    void realswap(void* x, void* y, size_t sizeX, size_t sizeY)
    {
    	if(sizeX != sizeY)
    	{
    		fprintf(stderr, "realswap: Size mismatch (%llu, %llu)\n", (unsigned long long)sizeX, (unsigned long long)sizeY);
    		abort();
    	}
    	else
    	{
    		size_t i;
    		char * xc = x;
    		char * yc = y;
    		for(i=0 ; i<sizeX ; i++)
    		{
    			char temp = xc[i];
    			xc[i] = yc[i];
    			yc[i] = temp;
    		}
    	}
    }
    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.

  13. #13
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 690
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 690
    Points : 30 984
    Points
    30 984
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    D'un autre côté, j'imagine bien un truc à base de macro pour faire un tel swap (mais ça aurait l'inconvénient d'évaluer chaque argument deux fois):
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    #define swap(x, y) realswap((x), (y), sizeof (x), sizeof (y))
    Ca fonctionne (je viens de le tester sur mon code précédent) mais avec une petite correction: #define swap(x, y) realswap((x), (y), sizeof *(x), sizeof *(y)). En effet, on passe les adresses des choses à swapper...

    Accessoirement ne vaut-il pas mieux sortir la déclaration de temp de la boucle ?
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  14. #14
    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
    Citation Envoyé par Sve@r Voir le message
    Accessoirement ne vaut-il pas mieux sortir la déclaration de temp de la boucle ?
    Franchement j'ignore si ça change grand-chose: seul un compilo vraiment primitif décalerait deux fois le pointeur de pile à chaque itération...

    Et puis, si on est à ce point-là dans la micro-optimisation, autant en profiter pour remplacer les indexages par des incrémentations de pointeur. Au détriment de la lisibilité, bien entendu.
    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.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Généricité : manipulation de void *
    Par Amroth dans le forum C
    Réponses: 5
    Dernier message: 05/11/2009, 00h33
  2. [architecture] pour de la généricité, vous feriez quoi ?
    Par Alec6 dans le forum Débats sur le développement - Le Best Of
    Réponses: 39
    Dernier message: 03/07/2006, 14h39
  3. Utilisation de void**
    Par KORTA dans le forum C
    Réponses: 6
    Dernier message: 05/09/2003, 19h52
  4. Manipulation de void*
    Par KORTA dans le forum C
    Réponses: 17
    Dernier message: 02/09/2003, 22h39
  5. TRaduction C++ => Delphi VOID ??
    Par escafr dans le forum Langage
    Réponses: 6
    Dernier message: 20/02/2003, 10h39

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