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

  1. #1
    Membre averti

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

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    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 : 2630
Taille : 12,4 Ko

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


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

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 580
    Points
    218 580
    Billets dans le blog
    120
    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.

  3. #3
    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
    Points : 6 498
    Points
    6 498
    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

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


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

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

    Informations forums :
    Inscription : Mai 2008
    Messages : 26 859
    Points : 218 580
    Points
    218 580
    Billets dans le blog
    120
    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.

  5. #5
    Membre éprouvé
    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
    Points : 1 211
    Points
    1 211
    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.

  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 689
    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 689
    Points : 30 983
    Points
    30 983
    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]

  7. #7
    Membre averti

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

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    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...

  8. #8
    En attente de confirmation mail

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

    Informations forums :
    Inscription : Septembre 2013
    Messages : 639
    Points : 2 347
    Points
    2 347
    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);

  9. #9
    Membre éprouvé
    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
    Points : 1 211
    Points
    1 211
    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é.

  10. #10
    Membre habitué
    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
    Points : 162
    Points
    162
    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.

  11. #11
    Membre éprouvé
    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
    Points : 1 211
    Points
    1 211
    Par défaut
    En effet, conceptuellement c'est l'élément qui "sait comment s'afficher" et non le maillon (j'ai simplifié au possible, après tout y'a bien des gens qui créent des genres de structures liste/maillon/élément ...).
    Pour compléter schonai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    struct object {
      // ...
    };
     
    struct monInt {
      struct object _base;
      // ...
    };
    Tu peux simplement déclarer un attribut de type struct object au début (et toujours au début !) de ton "type dérivé".

  12. #12
    Membre habitué
    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
    Points : 162
    Points
    162
    Par défaut
    L'appel direct des fonctions du type de base reste possible avec cette structure ?

  13. #13
    Membre éprouvé
    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
    Points : 1 211
    Points
    1 211
    Par défaut
    Oui, car l'agencement des membres de structure respecte l'ordre de déclaration.
    Les deux structures (celle que tu as proposé et la mienne) sont équivalentes dans leur représentation en mémoire.

  14. #14
    Membre habitué
    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
    Points : 162
    Points
    162
    Par défaut
    Ok, ta solution est donc plus élégante : pas de redondance, mise à jour, clarté.
    Pour un appel de fonction de base provenant du type de base (cast) sur un type dérivé (vous me suivez XD) il n'y a pas de problème car les membres existes et il y a correspondance d'adresse grâce à ce que tu as expliqué.
    Pour un appel de fonction de base provenant du type dérivé sur un type dérivé le membre fonction de base n'existe pas puisque il est contenus dans le membre _base. Du coup le compilateur doit hurler ?

  15. #15
    Membre averti

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

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut
    J'aimerais tellement utiliser les pointeurs de fonction, mais là, mes élèves ont déjà du mal avec le "concept des pointeurs" sur un type standard, je vais tenter d'introduire les différentes voies que vous avez illuminé de votre savoir-faire.
    C'est clair que j'adore l'idée d'une sorte de "super classe" objet et "dériver" (en C ça fait bizarre) les structures et types à partir de celle-ci. C'est beau.

    Je ne pensais pas arriver jusque là en posant la simple question "comment cela se fait-il que ma pseudo-solution fonctionne avec tous les types standards sauf avec le double ?", c'est vraiment merveilleux.
    Certains d'entre vous tous qui avez répondu m'ont montré des techniques auxquelles, à l'époque où moi-même j'apprenais à programmer en C (ça fait longtemps), je n'aurais jamais osé songer. Je n'ai vu l'orienté objet qu'après avoir appris le C.
    C'est marrant de constater que le voyage c --> c++ --> c rend le C aussi séduisant que le C++ (bon, toutes proportions gardées, ...)

    Il est vrai que je n'ai vraiment pas pensé "orienté objet" quand j'ai décidé de me lancer dans ce genre d'exercice. Je me suis focalisé sur la "mise en pratique" du passage par un pointeur de type void pour "démontrer" qu'il est possible de passer "n'importe quoi" à une fonction en langage C. Tout en sachant que l'organisme qui me paie ne me demande pas d'apprendre les techniques orientées objets à mes élèves. Disons que ce serait un bonus pour eux.

    Je devais faire cela sans "pointeur sur une fonction". J'y viendrais plus tard quand je serais certain que le "concept des pointeurs" ait été assimilé par mes élèves.
    Ils pourront améliorer leurs "containers" avec les pointeurs de fonction pour afficher les données contenues dans la liste chainée.

    Merci en tout cas pour les informations, je suis impressionné.

  16. #16
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par hurukan Voir le message
    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...
    Encore une fois, inutile. Si ton tableau fait 120 octets (par exemple un tableau de 15 doubles) te suffit de stocker ces 120 octets en vrac dans une zone d'au-moins 120 caractères. Quand tu les récupèreras de l'autre coté et que tu les replaceras dans un tableau analogue, tu retrouveras automatiquement tes 15 doubles. Exactement comme le fait memcpy() qui n'a absolument pas besoin de connaitre le type des éléments qu'elle copie d'une zone à l'autre. N'oublie pas qu'historiquement, avant le void*, c'était un char* qu'on utilisait pour manipuler des éléments inconnus...

    Citation Envoyé par hurukan Voir le message
    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.
    C'est cause du déréférencement. L'équivalence [] = * ne fonctionne que pour la première dimension d'un tableau (celle la plus à gauche). Prend l'exemple d'un échiquier que tu numéroterais de 1 (A1) à 64 (H8). Si tu veux ensuite connaitre le numéro de la case de la reine noire (4° colonne de la 8° ligne), tu calculeras de cette façon: 7 lignes vides * 8 (nombre de colonnes) + 4 = 60. Et tu te rends compte que tu es alors obligé de connaitre le nombre de colonnes de ton échiquier pour calculer cette position. Eh bien c'est pareil avec les tableaux. Toi tu travailles avec 2, 3, 4 dimensions mais l'ordi, lui, n'a qu'une seule ligne de cases mémoires et il est obligé de convertir les coordonnées [i][j] en position numérique dans sa ligne. Ainsi la case [i][j] est égale à i * nb_colonnes + j. Et on fait évoluer ce calcul quand le tableau a 3, 4, 5 dimensions. Ainsi la case [i][j][k] est égale à i * nb_de_cases_dimension2 + j * nb_cases_dimension3 + k. Il s'ensuit qu'une fonction qui reçoit un tableau en paramètre doit impérativement connaitre les dimensions dudit tableau (seule la première peut-être omise puisqu'elle n'est jamais utilisée dans le calcul)...


    Citation Envoyé par hurukan Voir le message
    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.
    Exact - Surtout si on se rappelle que tout malloc doit toujours être contrôlé et le cas d'échec géré. C'est donc bien plus contraignant...

    Citation Envoyé par hurukan Voir le message
    J'aimerais tellement utiliser les pointeurs de fonction, mais là, mes élèves ont déjà du mal avec le "concept des pointeurs" sur un type standard, je vais tenter d'introduire les différentes voies que vous avez illuminé de votre savoir-faire.
    Il est clair que tout ce qui a été dit ici ne peut s'appliquer qu'à une population ayant déjà une connaissance correcte des pointeurs !!!

    Citation Envoyé par hurukan Voir le message
    Je ne pensais pas arriver jusque là en posant la simple question "comment cela se fait-il que ma pseudo-solution fonctionne avec tous les types standards sauf avec le double ?", c'est vraiment merveilleux.
    J'ai pas examiné en détail ton code initial. Mais une chose est certaine en C: si ça fonctionne dans 3 cas standards et pas sur le 4°, alors ça veut dire qu'il y a une grosse erreur dans le code qui produit un comportement indéterminé mais que par chance (ou par malchance c'est selon la philosophie que l'on applique en pareil cas) le comportement semble correct dans les 3 cas. Et seul le 4° met en évidence qu'il y a un souci et qu'il faut impérativement le corriger. C'est alors tout sauf "merveilleux" !!!

    Citation Envoyé par hurukan Voir le message
    C'est marrant de constater que le voyage c --> c++ --> c rend le C aussi séduisant que le C++ (bon, toutes proportions gardées, ...)

    Il est vrai que je n'ai vraiment pas pensé "orienté objet" quand j'ai décidé de me lancer dans ce genre d'exercice. Je me suis focalisé sur la "mise en pratique" du passage par un pointeur de type void pour "démontrer" qu'il est possible de passer "n'importe quoi" à une fonction en langage C. Tout en sachant que l'organisme qui me paie ne me demande pas d'apprendre les techniques orientées objets à mes élèves. Disons que ce serait un bonus pour eux.
    A une époque je m'étais amusé à programmer un automate de vérification de tableau d'entiers sur des critères arbitraires choisis par l'utilisateur. Par exemple si l'utilisateur pouvait s'il avait envie demander à mon automate de vérifier que tous les éléments étaient pairs ou bien si tous les éléments étaient rangés en ordre croissant.
    J'avais pour cela créé une structure contenant
    • le libellé de la vérification faite
    • un pointeur sur la fonction dédiée à vérifier la chose demandée (fonction à programmer par l'utilisateur)
    • la valeur que devait renvoyer la fonction pour que le test soit validé. Par exemple si le test était que tous les éléments soient pairs, la fonction programmée renvoyait le nombre de pairs et l'automate attendait, pour ce test, que la fonction renvoie le nombre d'éléments du tableau.


    Une fois tous ces paramètres programmés et entrés, l'automate exécutait automatiquement tous les tests et renvoyait, pour chacun d'eux, un affichage indiquant si le test demandé était vérifié ou pas. Ca ne servait absolument à rien mais c'était un joli exemple sur les pointeurs et pointeurs de fonctions

    Voici le code retrouvé
    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
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    #include <sys/types.h>
    #include <stdio.h>
    #define NB		(3)		// Nombre d'éléments
     
    // Structure qui gère la question et la vérification de la réponse
    typedef struct {
    	char *txt;			// Texte à afficher
    	int (*verif)(int*);		// Fonction de vérification
    	int result;			// Résultat attendu
    } t_question;
     
    // Prototype des fonctions utilisées
    void saisie(int*);					// Saisie tableau
    int croissance(int*);				// Vérifie la croissance
    int impair(int*);					// Compte combien il y a d'impair
    int egalite(int*);					// Vérifie tous égaux
    int different(int*);				// Vérifie tous différents
     
    // Automate
    int main()
    {
    	int tabNB[NB];				// Tableau des nombres
    	static t_question tabQuestion[]={	 // Tableau des questions
     		{"Vérification croissance", croissance, 1},
     		{"Nombres tous impairs", impair, NB},
     		{"Nombres tous pairs", impair, 0},		// Avoir tous les nombres pairs revient a n'avoir aucun nombre impair... or la fonction qui cherche les impairs existe déjà...
     		{"Nombres tous égaux", egalite, 1},
     		{"Nombres tous différents", different, 1},
     		{NULL, NULL}
    	};
    	t_question *ptQuestion;    // Ptr balayage des questions
     
    	// On fait saisir les nombres
    	saisie(tabNB);
     
    	// Balayage des questions
    	for (ptQuestion=tabQuestion; ptQuestion->txt != NULL; ptQuestion++)
    	{
     		// On affiche l'action et son résultat
     		printf("%s %s\n", ptQuestion->txt, (ptQuestion->verif)(tabNB) == ptQuestion->result ?"ok" :"faux" );
    	}
    	return 0;
    }
     
    // Saisie tableau
    void saisie(int *tab)
    {
    	size_t i;			// Indice de boucle
    	char saisie[100];		// Zone de saisie temporaire
     
    	// On fait saisir les nombres
    	for (i=0; i < NB; i++)
    	{
    		while (1)
    		{
     			printf("Entrez le nombre %d/%d :", i + 1, NB);
     			fflush(stdout);
    			fgets(saisie, 100, stdin);
    			if (sscanf(saisie, "%d", tab+i) == 1) break;
    			printf("Saisie [%s] incorrecte - Recommencez\n", saisie);
    		}
    	}
    }
     
    // Fonction qui vérifie la croissance
    int croissance(int *tabNB)
    {
    	size_t i;			// Indice de boucle
     
    	// On balaye les nombres
    	for (i=1; i < NB; i++)
    	{
     		// On regarde si la croissance s'interromp
     		if (tabNB[i] <= tabNB[i - 1])
      			// La croissance n'est plus respectée
      			return 0;
    	}
    	// Tous croissants
    	return 1;
    }
     
    // Fonction qui compte le nombre d'impairs
    int impair(int *tabNB)
    {
    	size_t i;			// Indice de boucle
    	int cpt;			// Compteur des impairs
     
    	// On balaye les nombres
    	cpt=0;
    	for (i=0; i < NB; i++)
    	{
     		if ((tabNB[i] % 2) == 1)
      			// Le nombre est impair
      			cpt++;
    	}
     
    	// On renvoie le résultat
    	return cpt;
    }
     
    // Fonction qui vérifie qu'ils sont tous égaux
    int egalite(int *tabNB)
    {
    	size_t i;			// Indice de boucle
     
    	// On balaye les nombres
    	for (i=1; i < NB; i++)
    	{
     		// On regarde si le nombre est différent du premier
     		if (tabNB[i] != tabNB[0])
      			// L'égalité n'est plus respectée
      			return 0;
    	}
     
    	// Tous égaux
    	return 1;
    }
     
    // Fonction qui vérifie qu'ils sont tous différents
    int different(int *tabNB)
    {
    	size_t i;			// Indice de boucle
    	size_t j;			// Indice de boucle
     
    	// On balaye les nombres
    	for (i=1; i < NB; i++)
    	{
     		// On balaye les nombres situés avant celui en cours
     		for (j=0; j < i; j++)
     		{
      			// Si le nombre est égal à un des précédents
      			if (tabNB[i] == tabNB[j])
       				// L'inégalité n'est plus respectée
       				return 0;
    		}
    	}
     
    	// Tous inégaux
    	return 1;
    }

    J'en avais fait un autre dans le même esprit mais c'était un automate "questions/réponses". La structure contenait le libellé de la question et un pointeur de fonction (à programmer après) qui était sensé évaluer la réponse donnée par le candidat. Si par exemple la question était "quelle est la capitale de la France", la fonction d'évaluation pouvait alors accepter "Paris", "PARIS" ou "paris". L'automate se contentait, lui, deposer la question et de regarder si la fonction externe renvoyait 0 ou 1 pour refuser ou accepter la réponse comme valide et compter les points. Inutile tout autant mais démonstratif tout autant aussi. Mais lui je ne l'ai pas retrouvé...
    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]

  17. #17
    Membre éprouvé
    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
    Points : 1 211
    Points
    1 211
    Par défaut
    Citation Envoyé par schonai Voir le message
    Pour un appel de fonction de base provenant du type dérivé sur un type dérivé le membre fonction de base n'existe pas puisque il est contenus dans le membre _base. Du coup le compilateur doit hurler ?
    Je n'ai pas compris

  18. #18
    Membre averti

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

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Septembre 2012
    Messages : 313
    Points : 354
    Points
    354
    Par défaut
    Ca marche avec la méthode "The Hound" (pointeur sur une fonction pour l'affichage), cette fois les éléments de type "double" sont affichés sans problème... je pense que j'ai complètement déliré avec les memcpy()...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    void showDouble(void *pElem)
    {
    	double *pSurDouble=(double*)pElem;
    	printf("Double:\t%.3f\n",*pSurDouble);
    }
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    void	*vtmp=(void*)malloc(lc_parcours->dataSize);
    vtmp=lc_parcours->value;
    puis

    ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    case cssmdouble:
    				  //memcpy((void*)&tmpDouble,vtmp,sizeof(lc_parcours->dataSize));
    				  //printf("Valeur de l'élément %d --> %F\n",iIndex,tmpDouble);
    				  lc_parcours->pDisplay=showDouble;
    					lc_parcours->pDisplay(vtmp);
    				  break;
    ...
    fonctionne parfaitement...

  19. #19
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    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 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par hurukan Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    void	*vtmp=(void*)malloc(lc_parcours->dataSize);
    vtmp=lc_parcours->value;
    fonctionne parfaitement...
    Ca m'étonnerait. vtmp récupère une adresse allouée via malloc. A ce propos il est inutile, voire parfois dangereux, de caster un retour de malloc, surtout qu'en plus tu castes un void* (malloc renvoie déjà un void*) dans un void* !!!
    Puis tu perds cette adresse pour la remplacer par une autre valeur. Ca fonctionnera probablement (à condition déjà que tu n'aies pas besoin de la mémoire allouée) mais nous sommes loin du terme "parfaitement". Et de toute façon c'est une fuite mémoire assurée...
    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]

  20. #20
    Membre habitué
    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
    Points : 162
    Points
    162
    Par défaut
    Citation Envoyé par the Hound Voir le message
    Je n'ai pas compris
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    struct derive
    {
       struct base
       {
          int membreBase;
       } _base;
       int membreDerive;
    };
    Si j'ai bien compris ce que tu m'a dis je dois pouvoir faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    struct derive varDeriv;
     
    int i = varDeriv.membreBase;
    Sauf erreur de ma part c'est impossible. Tu es obligé de faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    struct derive varDeriv;
     
    int i = varDeriv._base.membreBase;
    On perd un peu de l'héritage à l'utilisation puisque qu'il faut préciser de qu'elle classe provient la définition de méthode. Après on a une architecture beaucoup plus robuste pour la programmation. A voir ce qui est le mieux =/

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