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 :

Le pointeur void (void*)


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre expérimenté

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    329
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 329
    Par défaut Le pointeur void (void*)
    Bonjour,

    Je suis en train de me prendre la tête sur ce code ci-dessous.
    Voici le principe (bon, je sais il y a toujours moyen de faire mieux, ...) je voudrais créer des listes chainées contenant vraiment n'importe quoi en C (un peu comme les templates en orienté objet).

    A priori cela fonctionne mais j'ai un soucis avec le fait qu'en C il n'est pas possible de "reconnaitre" le type de la variable, sizeof() ne nous renseigne que sur la taille qu'il occupe en mémoire.
    C'est pour cela que j'ai ajouté le champs dataType dans la structure, pour pouvoir savoir ce que je manipule.

    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
    enum{cssmbyte,cssmint,cssmlong,cssmfloat,cssmdouble,cssmldouble};
     
    typedef struct item_Datas
    {
    	int				item_Number;
    	int				dataSize;
    	int				dataType;
    	void				*value;
    	struct item_Datas	*pPrevious;
    	struct item_Datas	*pNext;
    }Datas;
     
    typedef struct 
    {
    	int		NbElem;
    	Datas	*pHead;
    }ListeChainee;

    Ne vous préoccupez pas des champs pPrevious pour le moment il ne sert pas à grand chose...

    J'ai une fonction qui sert à insérer à la tête de liste

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int lc_insert(void*,ListeChainee*,short,short);
    Le principe est le suivant, le passe une variable convertie en void* à la fonction, le pointeur sur la liste chainée, le type de la variable et la taille.
    La taille seule ne suffit pas, car un int et un float peuvent se confondre (chez moi la taille pour les deux types est 4 bytes).
    Pareil pour un double et un long, chez moi c'est 8 bytes.

    Et le soucis en C, à moins que j'aie raté un épisode, c'est que printf() a besoin de formater les données à afficher en fonction de la taille du type en question (il ne fera pas le même traitement pour un int ou un float).

    Pour tester mon programme voici les autres lignes de codes:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Datas* lc_get(ListeChainee*,int);
     
    void lc_showList(ListeChainee*);
     
    int		CodeDeRetour;
    int		element;
     
    int		Insertion1=256;
    long 	Insertion2=74004L;
    float	Insertion3=5.325;
    double	Insertion5=5455E-2;
    char	Insertion4='d';
    lc_get() permet d'extraire un élément de la liste.
    lc_showList() permet d'afficher des informations sur le contenu de la liste.

    Dans la fonction main, j'essaie d'insérer les éléments et puis j'essaie de relire les informations extraites des éléments de la liste et de les afficher.
    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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
     
    void main(void)
    {
      ListeChainee *pListeChainee=malloc(sizeof(ListeChainee));
      if(pListeChainee==NULL)
    	printf("Erreur d'allocation de mémoire...\n");
     
      CodeDeRetour=lc_insert((void*)&Insertion1,pListeChainee,cssmint,sizeof(int));
      CodeDeRetour=lc_insert((void*)&Insertion3,pListeChainee,cssmfloat,sizeof(float));
     
      CodeDeRetour=lc_insert((void*)&Insertion5,pListeChainee,cssmdouble,sizeof(double));
      CodeDeRetour=lc_insert((void*)&Insertion2,pListeChainee,cssmlong,sizeof(long));
     
      CodeDeRetour=lc_insert((void*)&Insertion4,pListeChainee,cssmbyte,sizeof(char));
     
      lc_showList(pListeChainee);
     
      // tentative d'affichage...
     
      Datas *lc_parcours=(Datas*)malloc(sizeof(Datas));
      element=0;
      while(element<pListeChainee->NbElem)
      {
    	  lc_parcours=lc_get(pListeChainee,element);
    	  if(lc_parcours==NULL) printf("Elément non trouvé...\n");
    	  else
    	  {
    		void	*vtmp=(void*)malloc(lc_parcours->dataSize);
    		vtmp=lc_parcours->value;
    		int		tmpInt;
    		long	tmpLong;
    		double	tmpDouble;
    		float	tmpFloat;
    		char	tmpChar;
    		switch(lc_parcours->dataType)
    		{
    			case cssmint: 
    				  memcpy((void*)&tmpInt,vtmp,sizeof(lc_parcours->dataSize));
    				  printf("Valeur de l'élément %d --> %d\n",element,tmpInt);
    				  break;
    			case cssmlong:
    				  memcpy((void*)&tmpLong,vtmp,sizeof(lc_parcours->dataSize));
    				  printf("Valeur de l'élément %d --> %ld\n",element,tmpLong);
    				  break;
    			case cssmdouble:
    				  memcpy((void*)&tmpDouble,vtmp,sizeof(lc_parcours->dataSize));
    				  printf("Valeur de l'élément %d --> %lf\n",element,tmpDouble);
    				  break;
    			case cssmbyte:
    				  memcpy((void*)&tmpChar,vtmp,sizeof(lc_parcours->dataSize));
    				  printf("Valeur de l'élément %d --> %c\n",element,tmpChar);
    				  break;
    			case cssmfloat:
    				  memcpy((void*)&tmpFloat,vtmp,sizeof(lc_parcours->dataSize));
    				  printf("Valeur de l'élément %d --> %f\n",element,tmpFloat);
    				  break;
     
    		}
    	  }
    	  element++;
      }
    }
    ...ça ne marche pas avec la variable de type double...

    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
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
     
    Datas* lc_get(ListeChainee *pListe,int position)
    {
      Datas *lc_parcours=pListe->pHead;
      while(lc_parcours!=NULL)
      {
    	if(lc_parcours->item_Number==position) return lc_parcours;
    	lc_parcours=lc_parcours->pNext;
      }
      return (Datas*)NULL;
    }
     
    int lc_insert(void *pData,ListeChainee *pListe,short type,short taille)
    {
      // l'insert se fera toujours en tête...
      if(pListe->pHead!=NULL)
      {
    	// Il y a un élément dans la liste
    	Datas *lc_new=(Datas*)malloc(sizeof(Datas));
    	pListe->pHead->pPrevious=lc_new;
     
    	lc_new->pPrevious=NULL;
    	lc_new->pNext=pListe->pHead;
    	lc_new->dataSize=taille;
    	lc_new->dataType=type;
    	lc_new->value=(void*)malloc(taille);
    	lc_new->item_Number=pListe->NbElem;
    	memcpy((void*)lc_new->value,pData,taille);
    	pListe->NbElem++;
    	pListe->pHead=lc_new;
      }
      else
      {
    	// La liste est vide
    	Datas	*lc_new=(Datas*)malloc(sizeof(Datas));
    	lc_new->pPrevious=NULL;
    	lc_new->pNext=NULL;
    	lc_new->dataSize=taille;
    	lc_new->dataType=type;
    	lc_new->value=(void*)malloc(taille);
    	memcpy((void*)lc_new->value,pData,taille);
    	lc_new->item_Number=pListe->NbElem;
    	pListe->NbElem++;
    	pListe->pHead=lc_new;
      }
    }
     
    void lc_showList(ListeChainee *pListe)
    {
      Datas *lc_parcours=pListe->pHead;
     
      while(lc_parcours!=NULL)
      {
    	printf("Adresse de l'élément : %08x\n",lc_parcours);
    	printf("Adresse de l'élément précédent: %08x\n",lc_parcours->pPrevious);
    	printf("Adresse de l'élément suivant: %08x\n",lc_parcours->pNext);
    	printf("Taille de l'élément : %d\n",lc_parcours->dataSize);
    	lc_parcours=lc_parcours->pNext;
      }
    }
    ...je suis content, il est possible de créer une liste qui peut contenir "n'importe quoi", mais le soucis c'est que sur un type standard, en l'occurrence le type "double", ça ne marche pas...

    Et je voudrais avoir une explication. Comme je ne manipule pas souvent des double, float etc... peut-être ais-je manqué quelque chose dans mon approche.
    Nom : listechainee.png
Affichages : 2946
Taille : 12,4 Ko

  2. #2
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 835
    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 835
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par hurukan Voir le message
    je voudrais créer des listes chainées contenant vraiment n'importe quoi en C (un peu comme les templates en orienté objet).

    A priori cela fonctionne mais j'ai un soucis avec le fait qu'en C il n'est pas possible de "reconnaitre" le type de la variable, sizeof() ne nous renseigne que sur la taille qu'il occupe en mémoire.
    C'est pour cela que j'ai ajouté le champs dataType dans la structure, pour pouvoir savoir ce que je manipule.
    Bonjour

    C'est une super idée de TP que de vouloir refaire les templates C++. Ta seule erreur est de vouloir en effet mémoriser le type.
    Inspire-toi de qsort(). Cette fonction reçoit le tableau à trier (normal), le nombre d'éléments à trier (encore normal), et surtout la taille d'un élément à manipuler. Nulle part la fonction n'a besoin de savoir qu'elle manipule des int ou des double. Si je devais créer une liste chainée générique c'est aussi ce que je ferais. Ma liste connaitrait juste la taille d'un élément à manipuler. Et en interne je ne manipulerais que des char en utilisant du memcpy().
    Surtout que tu veux stocker le type en partant du principe que t'auras que des types de base (int, char, long, etc). Mais si moi je crée un type perso contenant un nom, un prénom et une adresse tout ça dans une belle structure, ça va pas ête évident pour toi de prendre en compte ce cas...

    Et fais attention à sizeof car il est trompeur. sizeof(tableau) te donnera la taille du tableau (nb d'éléments * taille d'un élément) mais si tu passes ce tableau à une fonction, la fonction le stockera alors dans une adresse. Et si tu demandes sizeof(adresse) tu n'auras alors que 4 (taille d'une adresse)

    Exemple
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int main()
    {
        double tab[]={3.14, 2.718, 1.618};
        printf("%d\n", sizeof(tab));            // Donnera 3 éléments de 8 octets soit 24
        fct(tab);
    }
     
    void fct(double *tab)
    {
        printf("%d\n", sizeof(tab));            // Donnera 4 (la taille de l'adresse). Et c'est exactement pareil même si le prototype de la fonction est void fct(double tab[]).
    }

    Citation Envoyé par hurukan Voir le message
    Attention, main() est de type "int", pas "void" !!!

    Citation Envoyé par hurukan Voir le message
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    ListeChainee *pListeChainee=malloc(sizeof(ListeChainee));
    Pourquoi un malloc pour un seul élément ??? ListeChainee ma_liste;.
    Accessoirement tu devrais nommer tes types "t_xxx". Ca rendrait ton code plus lisible (qu'est-ce qui est type, qu'est ce qui est variable) et laisserait le token "xxx" libre pour des noms de variable
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    typedef struct 
    {
    	int		NbElem;
    	t_Datas	*pHead;
    } t_Liste;
     
    int main()
    {
        t_Liste ListeChainee;
        ...
    }
    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]

  3. #3
    Membre expérimenté

    Homme Profil pro
    Enseignant
    Inscrit en
    Septembre 2012
    Messages
    329
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 329
    Par défaut
    C'est vraiment magnifique de recevoir des réponses de passionnés... merci beaucoup !!
    Je vais potasser tout cela, et voir si ça apporte réponses à mes questions ^^

    Citation Envoyé par LittleWhite Voir le message
    Bonjour,

    D'après moi, la taille ne sert absolument à rien dans ce type de problème.
    À la place de la taille, si vous voulez vraiment conserver le type de la donnée, vous pouvez créer un enum, qui garde le type.

    Lorsque j'avais fait une implémentation similaire à la votre, je ne conservais absolument pas le type des données. C'était à l'utilisateur de s'en rappeler.
    Le très gros soucis avec ce genre de liste, c'est la libération de mémoire. Qui doit faire la libération et comment ? Si c'est la liste, comment elle sait qu'elle contient des pointeurs ou pas ? Et tout ce tas de question.
    Personnellement, depuis que j'ai découvert la GLib, je pense que je ne recoderai plus jamais ce type de structure, sauf pour jouer. En effet, la GLib est optimisé et fait les choses cent fois plus proprement que moi
    Merci LittleWhite.
    Si ça peut influencer d'autres personnes par rapport à cette problématique précise (utilisation du pointeur void* pour "passer n'importe quoi" à une fonction):

    Concernant la libération de la mémoire: je fais un free() lorsque je libère l'élément dans la fonction delete() je ne sais pas si c'est suffisant.

    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
    int lc_delete(ListeChainee *pListe,int target)
    {
      Datas *lc_parcours=(Datas*)malloc(sizeof(Datas));
      lc_parcours=pListe->pHead;
      while(lc_parcours!=NULL)
      {
    	if(lc_parcours->item_Number==target)
    	{
    	  lc_parcours->pPrevious->pNext=lc_parcours->pNext;
    	  pListe->NbElem--;
    	  free(lc_parcours);
    	  return 0;
    	}
    	lc_parcours=lc_parcours->pNext;  
      }
      return -1;
    }
    Concernant Glib: oui je pourrais utiliser une des nombreuses librairies liée au langage C, mais ici je tente une approche pédagogique pour mes élèves, et je dois dire que je sens que je vais avoir du mal avec cette partie du cours ^^

    Merci encore...

    Citation Envoyé par the Hound Voir le message
    Le lien que tu fais entre ta liste chaînée en C et un conteneur templaté en C++ est intéressant.

    Si l'idée est de construire une liste générique, il y a une différence entre les deux :
    - la liste chaînée C peut prendre "n'importe quoi",
    - le conteneur C++ peut prendre des objets de type T, T étant donné lors de l'instanciation du conteneur.
    C'est sémantiquement très différent. Le conteneur C++ conserve le type des éléments, pas la liste C.
    En C++, si tu voulais créer une liste chaînée pouvant prendre "n'importe quoi", tu aurais deux solutions :
    - soit faire comme tu le fais ici, utiliser un pointeur générique qui t'amène aux problèmes que tu exposes ici
    - créer une classe de base pour tous les éléments, et utiliser un conteneur de références de cette classe (Base* par ex.)
    Dans le dernier cas, tu perds le type réel de tes éléments, mais tu peux utiliser l'héritage pour appeler des fonctions communes à tes éléments, qui sont alors définies dans la classe de base.
    Si ce n'est pas déjà fait, je te suggère de te renseigner sur le virtual dispatch et comment ça fonctionne.
    ...
    Ici la liste peut prendre n'importe quoi, du moment que c'est "affichable". Tu noteras qu'on ne stocke plus (du moins directement) le type.
    Wow !
    Tu en as passé du temps là-dessus ^^ J'espère pas trop quand même !
    Je voulais justement éviter d'écrire des fonctions sur lesquelles un pointeur de fonction viendrait pointer, sans doute par paresse.
    Mais c'est clair que c'est plus séduisant d'avoir une fonction dédicacée à l'affichage de chaque type que cette liste chainée est censée manipuler.

    Ce que je ne comprends toujours pas, c'est pourquoi mon code fonctionne avec quasiment tous les types standards (et aussi les types et structures créés par mes soins pour les tests) et que le type "double", lui, ne "passe pas"...

    Je vais revoir ma copie de toutes façon...
    Merci encore !!

    Citation Envoyé par Sve@r Voir le message
    Bonjour

    ...

    Et fais attention à sizeof car il est trompeur. sizeof(tableau) te donnera la taille du tableau (nb d'éléments * taille d'un élément) mais si tu passes ce tableau à une fonction, la fonction le stockera alors dans une adresse. Et si tu demandes sizeof(adresse) tu n'auras alors que 4 (taille d'une adresse)
    Je ne connais pas d'autres méthodes pour connaître la taille d'un type en particulier. J'ai toujours utilisé sizeof() avec, comme paramètre, le nom d'un type ou d'une structure multiplié par le nombre d'éléments le cas échéant.
    Il est vrai que je me suis posé la question du fait que ma liste chainée ne pourra pas gérer des tableaux comme paramètre... en fait j'ai pas fait de tests, et à mon avis mon programme partirait en sucette ^^
    Je ne sais pas comment "détecter" qu'il s'agisse d'un tableau (ajouter un élément dans mon enum) et surtout il faudra probablement récupérer le type de donnée de ce tableau et le nombre d'éléments de celui-ci...

    Je vais essayer de placer une liste chainée dans mes éléments à insérer dans ma liste chainée si ça fonctionne, je pourrais créer un "tableau" à partir de cette liste chainée...

    Je sais que la taille des adresses dépend de l'architecture du microprocesseur et du système d'exploitation qui exploite les ressources: sur mon 64bits les adresses sont codées sur 8 bytes.
    J'ai déjà eu des soucis effectivement avec des "tailles d'éléments" du type int*[8] et int** qui n'ont évidemment pas la même taille: le premier c'est un tableau de pointeurs (8 pointeurs sur un int) et le second c'est un pointeur sur un pointeur (une adresse de 8 bytes sur mon ordinateur). Je pensais récupérer un int** alors qu'en réalité je recevais un int*[8] comme paramètre de ma fonction.

    Attention, main() est de type "int", pas "void" !!!
    Oui, c'est vrai. Merci de me le rappeler ^^


    Pourquoi un malloc pour un seul élément ??? ListeChainee ma_liste;.
    Accessoirement tu devrais nommer tes types "t_xxx". Ca rendrait ton code plus lisible (qu'est-ce qui est type, qu'est ce qui est variable) et laisserait le token "xxx" libre pour des noms de variable
    J'ai oublié la nomenclature à respecter dans la création des structures et des types, je me laisse aller à mon imagination ^^
    La question du malloc() pour un seul élément: je voulais passer la liste chainée comme paramètre sans devoir utiliser la syntaxe &ma_liste, c'est une fois de plus de la paresse.

    Merci encore...

  4. #4
    Membre Expert

    Profil pro
    Inscrit en
    Septembre 2013
    Messages
    639
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2013
    Messages : 639
    Par défaut
    Ah ! La joie d'écrire des conteneurs de void* en C !

    Pour ma part j'ai réglé le problème en stockant dans mes conteneurs des pointeurs de fonctions print, destroy, clone et compare.

    Par exemple si j'écris un module de liste chaînée de void*, la liste chaînée est une struct qui contient entre autres ces quatre pointeurs de fonctions qui servent respectivement à afficher un élément, désallouer un élément, cloner l'élément et comparer deux éléments.

    Quand je veux créer une nouvelle liste de TOTO vide (TOTO étant un type quelconque), je fais appel à une fonction "constructeur" et cet appel ressemble à :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    list l = empty_list_create (&fun_TOTO_print, &fun_TOTO_destroy, &fun_TOTO_clone, &fun_TOTO_compare);

  5. #5
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Par défaut
    Citation Envoyé par CodeurPlusPlus Voir le message
    Par exemple si j'écris un module de liste chaînée de void*, la liste chaînée est une struct qui contient entre autres ces quatre pointeurs de fonctions qui servent respectivement à afficher un élément, désallouer un élément, cloner l'élément et comparer deux éléments.

    Quand je veux créer une nouvelle liste de TOTO vide (TOTO étant un type quelconque), je fais appel à une fonction "constructeur" et cet appel ressemble à :

    list l = empty_list_create (&fun_TOTO_print, &fun_TOTO_destroy, &fun_TOTO_clone, &fun_TOTO_compare);
    C'est une bonne solution pour les comportements qui sont identiques quelque soit l'élément : libérer la mémoire, copier un maillon, etc.
    Par contre, quand un comportement est relatif au type de l'élément, cela ne résout pas le problème : dans ton exemple, si fun_TOTO_print ne sait pas de quel type est l'élément en question, elle ne pourra pas l'afficher ; on revient au problème initial.
    Une solution est de stocker le pointeur sur fonction dans le maillon et non dans la liste, comme dans l'exemple que j'ai proposé.

  6. #6
    Membre éprouvé
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2012
    Messages
    62
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2012
    Messages : 62
    Par défaut
    Bonjour,
    Cen'est pas au node de savoir comment l'élément s'affiche. C'est 'élément lui même qui le sait. Du coup void* n'est pas très adapté car ne correspond à aucune structure. Il faudrait plutôt utiliser une structure de base pour tout les types d'élément utilisé dans la liste.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    struct object
    {
         void print(); /* Affichage */
         stuct object* new(); /* Allocation + initialisation des méthodes */ 
         void free(struct object*); /* Libération */
    };
    En utilisant la méthode de The Hound on obtient une première classe de base. Ensuite on hérite...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    struct monInt
    {
         /* On reprend la structure de Objectdans le même ordre. */
         void print(); /* Affichage */
         stuct object* new(); /* Allocation + initialisation des méthodes */ 
         void free(struct object*); /* Libération */
        /*----------------------------------------*/
        /* On rajoute les données spécifique à notre classe fille.*/
         int data; 
         int add(int);
    };
    Il faut redéfinir les fonctions (méthodes) pour le nouveau type (classe) si des choses changent ou bien appeler la fonction (méthode) du type (classe) père.
    Bref on fait de l'orienté objet.

    Pour ta liste chainée, il suffit de remplacer ton void* par un struct object* et l'affichage se fait par node->data.print(node->data);
    Il n'y a pas d'erreur d'adressage des membres de la structure car leur définition sont commune et semblable. Par contre tu est le seul maître à bord. Le compilateur ne pourra pas t'indiquer certain problème qu'il détecte d'habitude et si tu utilises un débogueur comme GDB, il ne sera pas capable de retrouver le type réel de ta donnée.

  7. #7
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 129
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 129
    Billets dans le blog
    149
    Par défaut
    Bonjour,

    D'après moi, la taille ne sert absolument à rien dans ce type de problème.
    À la place de la taille, si vous voulez vraiment conserver le type de la donnée, vous pouvez créer un enum, qui garde le type.

    Lorsque j'avais fait une implémentation similaire à la votre, je ne conservais absolument pas le type des données. C'était à l'utilisateur de s'en rappeler.
    Le très gros soucis avec ce genre de liste, c'est la libération de mémoire. Qui doit faire la libération et comment ? Si c'est la liste, comment elle sait qu'elle contient des pointeurs ou pas ? Et tout ce tas de question.
    Personnellement, depuis que j'ai découvert la GLib, je pense que je ne recoderai plus jamais ce type de structure, sauf pour jouer. En effet, la GLib est optimisé et fait les choses cent fois plus proprement que moi
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  8. #8
    Rédacteur/Modérateur
    Avatar de Trap D
    Profil pro
    Inscrit en
    Septembre 2003
    Messages
    4 942
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2003
    Messages : 4 942
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Lorsque j'avais fait une implémentation similaire à la votre, je ne conservais absolument pas le type des données. C'était à l'utilisateur de s'en rappeler.
    Le très gros soucis avec ce genre de liste, c'est la libération de mémoire. Qui doit faire la libération et comment ? Si c'est la liste, comment elle sait qu'elle contient des pointeurs ou pas ? Et tout ce tas de question.
    Je me souviens avoir fait aussi ce genre de chose. J'avais "résolu" le problème en ajoutant au moment de la libération de mémoire un appel à une fonction utilisateur qui effectuait le travail sur les données ; de même pour une fonction qui affichait les données.
    "La haine seule fait des choix" - Koan Zen
    "Il ne faut pas être meilleur que les autres, il faut être meilleur que soi." Albert Jacquard
    "Ceux qui savent où ils ont posé leur parapluie ne sont pas alcooliques." - pgibonne.
    Faites du Prolog, ça vous changera les idées !
    Ma page Prolog
    Mes codes sources commentés

    Mon avatar : La Madeleine à la veilleuse de Georges de La Tour

  9. #9
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 129
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 129
    Billets dans le blog
    149
    Par défaut
    Moi, je passais un booléan, pour savoir si la liste devait libéré elle même les données, mais ça reste très "dangereux".
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  10. #10
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2014
    Messages
    345
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Finance

    Informations forums :
    Inscription : Juin 2014
    Messages : 345
    Par défaut
    Le lien que tu fais entre ta liste chaînée en C et un conteneur templaté en C++ est intéressant.

    Si l'idée est de construire une liste générique, il y a une différence entre les deux :
    - la liste chaînée C peut prendre "n'importe quoi",
    - le conteneur C++ peut prendre des objets de type T, T étant donné lors de l'instanciation du conteneur.
    C'est sémantiquement très différent. Le conteneur C++ conserve le type des éléments, pas la liste C.
    En C++, si tu voulais créer une liste chaînée pouvant prendre "n'importe quoi", tu aurais deux solutions :
    - soit faire comme tu le fais ici, utiliser un pointeur générique qui t'amène aux problèmes que tu exposes ici
    - créer une classe de base pour tous les éléments, et utiliser un conteneur de références de cette classe (Base* par ex.)
    Dans le dernier cas, tu perds le type réel de tes éléments, mais tu peux utiliser l'héritage pour appeler des fonctions communes à tes éléments, qui sont alors définies dans la classe de base.
    Si ce n'est pas déjà fait, je te suggère de te renseigner sur le virtual dispatch et comment ça fonctionne.

    Bref, tout ça pour dire que en fonction du contexte, tu pourrais utiliser les principes de l'orienté objet pour pallier à ton problème. Exemple (simplifié) :

    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
    typedef struct node_s {
      struct node_s *prev, *next;
      void* data; // Données
      void (*display)(); // Pointeur sur la fonction d'affichage
    } node_t;
     
    // Affiche un int
    void display_int(node_t* node) {
      printf("%d", *((int*) node->data));
    }
     
    void display_float(node_t* node) {
      printf("%f", *((float*) node->data));
    }
     
    // Crée un entier
    node_t* create_int(int x) {
      node_t* node;
      // Malloc et tout le tralala
      *((int*) node->data) = x;
      node->display = display_int;
      return node;
    }
     
    // Pareil pour float, etc.
     
    // Parcourt la liste et affiche les éléments
    void display_list(list_t* list) {
      // ... Boucle ...
        node->display(node);
        // Si node a été construit par create_int(), node->display réfère à display_int()
        // Même chose mais avec float, double, mytotostruct, etc.
    }
    Ici la liste peut prendre n'importe quoi, du moment que c'est "affichable". Tu noteras qu'on ne stocke plus (du moins directement) le type.

Discussions similaires

  1. Réponses: 1
    Dernier message: 02/11/2013, 16h40
  2. Pointeur sur void
    Par boss89 dans le forum C
    Réponses: 1
    Dernier message: 03/01/2013, 23h22
  3. portabilité du pointeur générique void*
    Par uknow dans le forum C
    Réponses: 6
    Dernier message: 09/01/2011, 15h48
  4. Réponses: 4
    Dernier message: 11/08/2008, 14h49
  5. Pointeur générique (void *)
    Par c-candide dans le forum C
    Réponses: 22
    Dernier message: 25/02/2007, 02h52

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