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 :

Hashmap POSIX <hsearch.h>


Sujet :

C

  1. #1
    Membre habitué Avatar de Echyzen
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2012
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2012
    Messages : 123
    Points : 178
    Points
    178
    Par défaut Hashmap POSIX <hsearch.h>
    Salut tout le monde,

    J'ai une question assez spécifique je suis pas sûr que l'on puisse m'aider mais je tente quand même alors voilà j'utilise les hashmap proposé par POSIX fichier <search.h>.

    Le problème c'est qu'au bout d'un moment et pourtant je n'ai qu'une centaine de mot il remet le compteur à zéro sans que je comprenne pourquoi c'est à dire :
    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
     
    /* ajoute word dans la table */
    void hash_table_add(char *word) {
    		e.key = word;
     
    		/*
    		* Test si le mot n'est pas dans notre HashTable
    		* auquel cas on le rajoute
    		*/
    		if((ep = hsearch(e, FIND)) == NULL) {
    			/* e.data est un int
    			* permet d'associer la key de manière unique
    			*/
    			printf("%s\n",e.key);
    			e.data = (void *) 1;
    			ep = hsearch(e, ENTER);
    			/* there should be no failures */
    			if (ep == NULL) {
    				fprintf(stderr, "entry failed\n");
    				exit(EXIT_FAILURE);
    			}
    		} else { // sinon on incrémente son data de 1
    			ep = hsearch(e, FIND);
    			/*int data = (int) ep->data + 1;
    			ep->data = (void*) data;*/
    			ep->data += 1;
    			hsearch(*ep,ENTER);			
    		}
     
     
    }
    J'appelle ma fonction est moment il va plus trouver un mot qui est pourtant bien dans la table et il va donc réinitialisé le compteur...

  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
    Je ne connais pas ce module, mais cette ligne m'étonne :
    Ton objectif est de copier la chaine ?
    Si oui, ce n'est pas la bonne méthode, il faut utiliser strcpy().
    Si non, comment t'assures-tu de la pérennité du "lien" ?

  3. #3
    Membre expert
    Avatar de Metalman
    Homme Profil pro
    Enseignant-Chercheur
    Inscrit en
    Juin 2005
    Messages
    1 049
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Enseignant-Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 049
    Points : 3 532
    Points
    3 532
    Par défaut
    Peut être pas un strcpy, mais un strdup... enfin à tester !

    http://pubs.opengroup.org/onlinepubs.../search.h.html

    Mais d'après l'OpenGroup, le key est bien un char*...
    Il faudrait juste savoir où est défini "e", ce qu'est "ep", etc...
    Peut être un effet de bord ?
    --
    Metalman !

    Attendez 5 mins après mes posts... les EDIT vont vite avec moi...
    Les flags de la vie : gcc -W -Wall -Werror -ansi -pedantic mes_sources.c
    gcc -Wall -Wextra -Werror -std=c99 -pedantic mes_sources.c
    (ANSI retire quelques fonctions comme strdup...)
    L'outil de la vie : valgrind --show-reachable=yes --leak-check=full ./mon_programme
    Et s'assurer que la logique est bonne "aussi" !

    Ma page Developpez.net

  4. #4
    Membre habitué Avatar de Echyzen
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2012
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2012
    Messages : 123
    Points : 178
    Points
    178
    Par défaut
    Effectivement, j'ai omis des informations alors je mets ce qui est demandé :

    ENTRY e, *ep;

    Ceci est au dessus en début de fichier (étant issus du C++ j'appelle ceci le namespace anonyme). J'ai mis un strcpy à la place et j'ai toujours le même problème peut être faut il que je fasse un malloc mais je ne sais comment m'y prendre...

    Je suis vraiment perdu là...

  5. #5
    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
    Quand on est vraiment perdu, on s'arrête et on lit une carte.

    En C, la carte, ce sont les pages de manuel --> http://linux.die.net/man/3/hsearch On y lit :
    The argument item is of type ENTRY, which is defined in <search.h> as follows:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    typedef struct entry {
        char *key;
        void *data;
    } ENTRY;
    En faisant donc e.key = word;, tu fais juste l'association de pointeurs. Mais si la zone pointée par word devient invalide (parce qu'on appelle free() ou passe que c'est un tableau automatique), alors ton entrée permet sa clé. Je ne sais pas comment tu l'utilises cette entrée, mais il faut voir si ça a une importance. Si tu l'ajoutes à la map, je pense que oui.

    Ceci est au dessus en début de fichier (étant issus du C++ j'appelle ceci le namespace anonyme).
    Tu n'ignores sans doute pas qu'il n'y a pas de notion de namespace en C, juste des notions de portée. Si ta variable ne possède pas le modification static, alors elle est globale à tout le programme.

    J'ai mis un strcpy à la place et j'ai toujours le même problème peut être faut il que je fasse un malloc mais je ne sais comment m'y prendre...
    Tu ne sais pas comment t'y prendre pour faire un malloc() ?
    Doit-on ou ne doit-on pas faire un malloc() avant de faire un strcpy() ? Hum.... La définition de la structure réserve t-elle un espace pour pouvoir y stocker quoi que ce soit ? Que penses-tu du code suivant ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    char *p;
    strcpy(p, "bonjour");
    @Metalman : je n'ai pas parlé de strdup() car ce n'est pas une fonction standard. Et après, j'ai réalisé que c'est une fonction POSIX, ce qui n'est pas vraiment un problème quand on travaille avec des hash maps POSIX

  6. #6
    Membre expert
    Avatar de Metalman
    Homme Profil pro
    Enseignant-Chercheur
    Inscrit en
    Juin 2005
    Messages
    1 049
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Enseignant-Chercheur
    Secteur : Enseignement

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 049
    Points : 3 532
    Points
    3 532
    Par défaut
    @Bk : strdup()... si je l'utilise... c'est vraiment parce qu'elle m'a été TRES utile pour debugger/pour me faciliter la vie... mais que c'est bien chiant de toujours se prendre un "undefined strdup" avec le flag ansi !
    Bref la zone floue de ANSI/POSIX avec strdup est quasiment inévitable à chaque fois.

    @Echyzen : Etrange que tu ais autant de difficultés avec malloc/free....
    Parce que c'est quasiment la même chose que new/delete lors de l'utilisation avec les types.
    Tu en as déjà fait, non ? Les pointeurs toossa ça te dit quelquechose, non ?
    --
    Metalman !

    Attendez 5 mins après mes posts... les EDIT vont vite avec moi...
    Les flags de la vie : gcc -W -Wall -Werror -ansi -pedantic mes_sources.c
    gcc -Wall -Wextra -Werror -std=c99 -pedantic mes_sources.c
    (ANSI retire quelques fonctions comme strdup...)
    L'outil de la vie : valgrind --show-reachable=yes --leak-check=full ./mon_programme
    Et s'assurer que la logique est bonne "aussi" !

    Ma page Developpez.net

  7. #7
    Membre habitué Avatar de Echyzen
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2012
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2012
    Messages : 123
    Points : 178
    Points
    178
    Par défaut
    Merci les amis vous avez résolus la plupart de mes problèmes il m'en reste plus qu'un et j'y suis bloquer après mainte essaye de deboggage.

    Je trouve des mots dans mon hashmap que je n'ai jamais ajouté et j'en ai la preuve par les print. Je vous montre les codes impliqué dans cette partie du problème :

    On commence avec le main de test qui trouve le mot "wobe" alors qu'il ne fait pas partie du text que je prend en référentiel
    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
     
    #include <stdio.h>
    #include <stdlib.h>
    #include "hash.h"
    #include "corpus.h"
    #include "correct.h"
     
    int main(int argc, char* argv[])
    {
      if (argc != 2) {
        fprintf(stderr, "Usage: %s mot-a-corriger\n", *argv);
        exit(EXIT_FAILURE);
      }
     
      /* Créer la table de hash */
      hash_table_create();
     
      /* Initialiser le corpus */
      if (! init_corpus_from_file("Data/Iliad.txt"))
        return EXIT_FAILURE;
    		  printf("occurences de wobe: %d (9)\n",     hash_table_search("wobe"));
      /* proposer une correction éventuelle */
      printf("%s\n", argv[1]);
      char * test = malloc (13* sizeof(char));
      strcpy(test, correct_word(argv[1]));
      printf("%s ==> %s\n", argv[1], test);
     
      /* Terminer */
      hash_table_destroy();
      return EXIT_SUCCESS;
    }
    Est voila ma fonction search en question qui trouve quelques chose qui n'y est pas et add qui marche étant donnée qu'elle ne l'ajoute pas...
    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
     
    /* renvoie le nombre d'occurences de word dans la table (0 si absent) */
    int hash_table_search(char *word) {
     
    	strcpy(e.key, word);
     
    	ep = hsearch(e, FIND);
    	if (ep == NULL)
    		return 0;
    	else
    		return (int)(ep->data);
    }
     
    /* ajoute word dans la table */
    void hash_table_add(char *word) {
     
    		strcpy(e.key, word);
    		/*
    		* Test si le mot n'est pas dans notre HashTable
    		* auquel cas on le rajoute
    		*/
    		if(((ep = hsearch(e, FIND)) == NULL) && (hash_table_search(e.key) == 0)) {
    			/* e.data est un int
    			* permet d'associer la key de manière unique
    			*/
    			e.data = (void *) 1;
    				printf("\ne.key : %s\n", e.key);
    			ep = hsearch(e, ENTER);
    			/* there should be no failures */
    			if (ep == NULL) {
    				fprintf(stderr, "entry failed\n");
    				exit(EXIT_FAILURE);
    			}
    		} else { // sinon on incrémente son data de 1
    			ep = hsearch(e, FIND);
    			ep->data += 1;
    			hsearch(*ep,ENTER);	
    		}
     
     
    }
    Comme le projet commence a grandir je ne sais si vous pourriez m'aider mais vous avez l'air de gérer le C

  8. #8
    Membre habitué Avatar de Echyzen
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2012
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2012
    Messages : 123
    Points : 178
    Points
    178
    Par défaut
    Re

    Alors j'ai remarqué qu'il me reste un warning et que ce dernier peut être la cause de mon problème...

    En effet, ep->data est un void * (un pointeur de entry) mais je lui envoie que des int est vice ver-çà. Or, il y a un moment je doit convertir le void * (dont je suis sûr qu'il pointe sur un int) en int. Actuellement je fais : return (int)(ep->data); (Cf code précédent)

    Il m'affiche un warning mais récupère bien le int j'ai essayer d'autre type de conversion qui me font des segmentation fault carrément....

    Si vous savez comment convertir un void * en int de manière propre je suis preneur

  9. #9
    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 Echyzen Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    /* ajoute word dans la table */
    void hash_table_add(char *word) {
    ...
      e.data = (void *) 1;
    ...
    }
    Ne faut-il pas allouer aussi dans le tas un espace pour stocker ton int ? J'avoue n'avoir utiliser cette façon d'écrire un transtypage d'un nombre mais si je comprends bien tu transforme le chiffre 1 en un pointeur non typé. e.data doit certainement pointé sur l'adresse 1 de ta machine. Enfin il me semble...

    J'aurais plutôt fait un truc du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    e.data = malloc (sizeof(int));
    *e.data = 1;

  10. #10
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Il m'affiche un warning mais récupère bien le int j'ai essayer d'autre type de conversion qui me font des segmentation fault carrément....
    Ca aurait été trop facile de nous copier-coller le warning de ton compilateur je présume ? Tu préfères nous challenger ?
    En tout cas, je trouve ça louche que ça te fasse une erreur de segmentation avec autre chose. Ou alors ce autre chose est vraiment faux.

    Pour tous les deux, vous avez peut-être vu l'exemple donner dans le man : http://linux.die.net/man/3/hsearch Il y a bien des convertions de void* en int. Ca fait un peu sale, mais comme le C ne possède pas de type générique autre que void* (on aimerait bien utiliser un objet comme Object de Java...), je ne pense pas qu'il y ait une autre solution plus efficace. Ton idée gerald3d semble en effet plus propre : on alloue un int, on donne le pointeur facilement, on récupère le pointeur, on déréférence, et voilà. Sauf que ça fait déclarer allouer un entier qui ne sert pas à grand chose. Autant caster violemment le pointeur en entier. Ca sous-entend juste qu'un pointeur fait au moins la taille d'un int. Faudrait voir ce que POSIX dit à ce sujet ^^ Soyons fous et supposons le code du man comme juste ! --> grim7reaper montre plus loin que ce n'est peut-être pas une bonne idée.

    Comme le projet commence a grandir je ne sais si vous pourriez m'aider mais vous avez l'air de gérer le C
    Pour commencer, vire donc cette variable globale e qui ne sert à rien dans l'exemple que tu montres.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if(((ep = hsearch(e, FIND)) == NULL) && (hash_table_search(e.key) == 0))
    Y a t-il la moindre chance que ta fonction trouve plus ? Vu qu'elle appelle hsaerch() de la même manière, j'en doute. Plus globalement, je suis moyennement convaincu par ton "surfaçage" des méthodes. Surtout quand je vois exit(EXIT_FAILURE); au milieu de la fonction d'ajout. Elle devrait renvoyer un code d'erreur et l'appelant décide si le programme doit s'arrêter ou pas. C'est violent un exit(), je sais pas si tu sais

    En faisant donc e.key = word;, tu fais juste l'association de pointeurs. Mais si la zone pointée par word devient invalide (parce qu'on appelle free() ou passe que c'est un tableau automatique), alors ton entrée permet sa clé. Je ne sais pas comment tu l'utilises cette entrée, mais il faut voir si ça a une importance. Si tu l'ajoutes à la map, je pense que oui.
    J'ai dit ça mais ça ne voulait pas dire "fait forcément un strcpy()", ça voulait dire : vérifie bien ce que tu ajoutes.

    En fait, je pense que tu pourras chercher n'importe quel mot et le trouver. Je me trompe ? Pourrais-tu faire le test ?

  11. #11
    Membre habitué Avatar de Echyzen
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2012
    Messages
    123
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2012
    Messages : 123
    Points : 178
    Points
    178
    Par défaut
    Non il me trouve pas n'importe quel mot

    Et effectivement il redondance dans le code...
    Pour ce qui est du exit ne t'inquiète pas, c'est juste pour déboguer

  12. #12
    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
    Tu peux utliser assert() pour déboguer, c'est fait pour

    J'ai fait ce code :
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <search.h>
     
    ENTRY e;
     
    ENTRY* add(const char *key)
    {
        puts(__func__);
        strcpy(e.key, key);
        ENTRY *res = hsearch(e, ENTER);
        return res;
    }
     
    ENTRY* search(const char *key)
    {
        puts(__func__);
        strcpy(e.key, key);
        ENTRY *res = hsearch(e, FIND);
        return res;
    }
     
    int main(void)
    {
        hcreate(10);
     
        e.key = malloc(1000);
     
        const char *bonjour = "bonjour";
        const char *woke = "woke";
     
        ENTRY *one = add(bonjour);
        ENTRY *two = add(woke);
        ENTRY *three = search(bonjour);
     
        printf("ENTRY @ %p\n", e.key);
     
        if(one)
            printf("%p %s\n", one->key, one->key);
        else
            puts("NULL");
     
        if(two)
            printf("%p %s\n", two->key, two->key);
        else
            puts("NULL");
     
        if(three)
            printf("%p %s\n", three->key, three->key);
        else
            puts("NULL");
     
        free(e.key);
        //hdestroy();
        return 0;
    }
    J'obtiens cela :
    add
    add
    search
    ENTRY @ 0x10a3009a0
    0x10a3009a0 bonjour
    0x10a3009a0 bonjour
    0x10a3009a0 bonjour
    Je pense être dans le même cas de figure que toi. Je m'attendrais à ce qu'il affiche bonjour / woke / bonjour avec les 3 derniers printf(). Je me demande si ta variable globale ne sert pas à toutes les entrées dans la map et donc changer sa clé change toutes les entrées contenues dans la map et ajoutées avec cette variable globale.

  13. #13
    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
    De mon côté j'ai repris le code exemple dans le man pour le simplifier au maximum. J'ai crée une table de 3 éléments puis j'ai tenté d'afficher 4 éléments avec 4 clefs dont une n'est bien sûre pas présente dans la htable.

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <search.h>
     
     
    int main(int argc, char *argv[])
    {
    	char *key[] = { "alpha", "bravo", "charlie", "delta" };
    	ENTRY *ep;
    	ENTRY e;
    	int i;
     
    	// Création d'un table de 3 éléments
    	hcreate(3);
     
    	// Insertion d'un élément
    	e.key = key[0];
    	e.data = (void*)1;
    	ep = hsearch(e, ENTER);
     
    	// Insertion d'un élément
    	e.key = key[1];
    	e.data = (void*)2;
    	ep = hsearch(e, ENTER);
     
    	// Insertion d'un élément
    	e.key = key[2];
    	e.data = (void*)3;
    	ep = hsearch(e, ENTER);
     
    	// Maintenant nous scrutons toute la table pour afficher les données en
    	// fonction des différentes clefs. La clef n°4 "delta" n'existe pas dans
    	// la table.
    	for (i=0; i<4; i++)
    		{
    			// On recheche dans la table une donnée dont la clef est key[i]
    			e.key = key[i];
    			ep = hsearch(e, FIND);
     
    			if (ep)
    				{ // Un élément est trouvé pour une clef donnée
    					printf("Un élément est trouvé pour key = %s, data = %d\n", ep->key, (int)ep->data);
    				}
    			else  // Aucun élément n'a été trouvé
    				printf("Aucun élément n'est trouvé pour  key = %s\n", key[i]);
    		}
     
    	// Libération mémoire de la table
    	hdestroy();
     
    	return 0;
    }
    Ce code donne comme résultat :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Un élément est trouvé pour key = alpha, data = 1
    Un élément est trouvé pour key = bravo, data = 2
    Un élément est trouvé pour key = charlie, data = 3
    Aucun élément n'est trouvé pour  key = delta

  14. #14
    Membre éclairé
    Inscrit en
    Juillet 2012
    Messages
    231
    Détails du profil
    Informations forums :
    Inscription : Juillet 2012
    Messages : 231
    Points : 870
    Points
    870
    Par défaut
    Citation Envoyé par Bktero
    Ca sous-entend juste qu'un pointeur fait au moins la taille d'un int. Faudrait voir ce que POSIX dit à ce sujet ^^
    Étant donné que c’est une fonction POSIX et non une fonction standard du C je ne vais pas me référer à la norme pour ce qui est des tailles de types.
    Pour ce qui est de POSIX, pour un int on a :
    Citation Envoyé par http://pubs.opengroup.org/onlinepubs/007904875/basedefs/limits.h.html
    {INT_MAX}
    Maximum value of an int.
    [CX] [Option Start] Minimum Acceptable Value: 2 147 483 647
    Ce qui implique que dans POSIX, un int c’est au minimum 32-bit.
    Pour ce qui est de la taille d’un pointeur, il suffit de regarder la taille requise pourintptr_t (ou uintptr_t). En effet, ces types doivent (par définition) être suffisamment large pour que l’on puisse mettre un pointeur dedans et le récupérer sans aucun souci :
    Citation Envoyé par http://pubs.opengroup.org/onlinepubs/007904875/basedefs/stdint.h.html
    The following type designates a signed integer type with the property that any valid pointer to void can be converted to this type, then converted back to a pointer to void, and the result will compare equal to the original pointer: intptr_t

    The following type designates an unsigned integer type with the property that any valid pointer to void can be converted to this type, then converted back to a pointer to void, and the result will compare equal to the original pointer: uintptr_t
    Les contraintes de tailles imposées par POSIX sont :
    Citation Envoyé par http://pubs.opengroup.org/onlinepubs/007904875/basedefs/stdint.h.html
    Limits of integer types capable of holding object pointers

    Minimum value of pointer-holding signed integer type:

    {INTPTR_MIN}
    -(2^15 -1)

    Maximum value of pointer-holding signed integer type:

    {INTPTR_MAX}
    2^15 -1

    Maximum value of pointer-holding unsigned integer type:

    {UINTPTR_MAX}
    2^16 -1
    On en déduit donc qu’un pointeur peut faire 16-bit.

    Du coup, selon POSIX un pointeur c’est au minimum 16-bit et un entier c’est au minimum 32-bit donc rien ne garanti que la conversion soit sûre.

    Au passage, je rappelle qu’une conversion void*->int->void* qui se passe mal c’est un Undefined Behavior :
    Citation Envoyé par ISO/IEC 9899:TC3, 6.3.2.3 Pointers, page 47
    5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.

    6 Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.
    Donc personnellement, j’éviterai de jouer avec ça (même si dans la pratique, ça semble fonctionner).

  15. #15
    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
    @gerald : au lieu de faire e.key = key[0];, pourrais-tu tester strpcy(e.key, key[0]); en ayant bien sûr fait un malloc() sur e.key ? Je n'ai pas Linux sous la main pour tester moi-même.

    @grim7reaper : merci pour cette recherche !

    C'est donc perdu pour la portabilité. Il est un peu chelou cet exemple du man quand même...

    void*->int->void*
    Ici, il s'agit plutôt de int -> void* -> int, on risque uniquement la perte d'information et non de se retrouver avec un pointeur invalide qui va provoquer un crash. Une idée pour une meilleure portabilité de l'exemple, bien que ce ne soit pas non plus très beau, serait d'utiliser intptr_t (ou uintptr_t) plutôt que int. Il y aurait au moins cohérence dans les conversions.

    Donc personnellement, j’éviterai de jouer avec ça (même si dans la pratique, ça semble fonctionner).
    Que ferais-tu alors ? Allouer un int à chaque fois et passer le pointeur vers cet int à la map ?

  16. #16
    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
    Pour faire plaisir à tout le monde j'ai modifié mon exemple en allouant dans le tas la clef et le data de chaque entrée dans la htable. Ainsi plus de warning lié au problème de taille entre un int et un pointeur. Le cas est plus propre me semble-t-il...

    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
    63
    64
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <search.h>
     
     
    int main(int argc, char *argv[])
    {
    	char *key[] = { "alpha", "bravo", "charlie", "delta" };
    	ENTRY *ep;
    	ENTRY e;
    	int i;
     
    	// Création d'un table de 3 éléments
    	hcreate(3);
     
    	// Insertion d'un élément
    	e.key = malloc (strlen(key[0])); // Allocation mémoire dans le tas pour la clef
    	e.key = strcpy(e.key, key[0]);   // Assignation de la valeur à la clef
    	e.data = malloc (sizeof(int));   // Allocation mémoire dans le tas pour le data
    	*(int*)e.data = 1;               // Assignation dela valeur à data
    	ep = hsearch(e, ENTER);          // Insertion dans la htable
     
    	// Insertion d'un élément
    	e.key = malloc (strlen(key[1]));
    	e.key = strcpy(e.key, key[1]);
    	e.data = malloc (sizeof(int));
    	*(int*)e.data = 2;
    	ep = hsearch(e, ENTER);
     
    	// Insertion d'un élément
    	e.key = malloc (strlen(key[2]));
    	e.key = strcpy(e.key, key[2]);
    	e.data = malloc (sizeof(int));
    	*(int*)e.data = 3;
    	ep = hsearch(e, ENTER);
     
    	// Maintenant nous scrutons toute la table pour afficher les données en
    	// fonction des différentes clefs. La clef n°4 "delta" n'existe pas dans
    	// la table.
    	for (i=0; i<4; i++)
    		{
    			// On recheche dans la table une donnée dont la clef est key[i]
    			e.key = key[i];
    			ep = hsearch(e, FIND);
     
    			if (ep)
    				{ // Un élément est trouvé pour une clef donnée
    					printf("Un élément est trouvé pour key = %s, data = %d\n", ep->key, *(int*)ep->data);
     
    					// On libére la mémoire de ep.key et ep.data. Ainsi à la sortie de la boucle
    					// il suffira de libérer la mémoire de la structure de htable pour finir le travail ;)
    					free (ep->key);
    					free (ep->data);
    				}
    			else  // Aucun élément n'a été trouvé
    				printf("Aucun élément n'est trouvé pour key = %s\n", key[i]);
    		}
     
    	// Libération mémoire de la table devenue vide
    	hdestroy();
     
    	return 0;
    }

  17. #17
    Membre éclairé
    Inscrit en
    Juillet 2012
    Messages
    231
    Détails du profil
    Informations forums :
    Inscription : Juillet 2012
    Messages : 231
    Points : 870
    Points
    870
    Par défaut
    Citation Envoyé par Bktero
    @grim7reaper : merci pour cette recherche !
    De rien

    Citation Envoyé par Bktero
    C'est donc perdu pour la portabilité. Il est un peu chelou cet exemple du man quand même...
    Je pense que ça vient du fait que, en pratique, ça fonctionne. La GLib propose de faire ça aussi.
    Toutes les plateformes que j’ai utilisé jusqu’à maintenant avait un sizeof(pointeur) >= sizeof(int). Et si tu regardes cette page tu vois que tout les modèles de données (ILP32, LP32, LP64, ILP64 et LLP64) respecte cette contrainte.

    Mais rien ne le garantit. Ni dans le standard, ni dans POSIX : tu n’es pas à l’abri d’une plateforme où ça ne passera pas.


    Citation Envoyé par Bktero
    Ici, il s'agit plutôt de int -> void* -> int
    Même souci : à partir du moment où tu fais void*->int (pointeur => entier) tu te retrouves dans le cas 6 cité dans la norme et il y a un risque de comportement indéfini.

    Citation Envoyé par Bktero
    Une idée pour une meilleure portabilité de l'exemple, bien que ce ne soit pas non plus très beau, serait d'utiliser intptr_t (ou uintptr_t) plutôt que int. Il y aurait au moins cohérence dans les conversions.
    C’est une solution en effet.


    Citation Envoyé par Bktero
    Que ferais-tu alors ? Allouer un int à chaque fois et passer le pointeur vers cet int à la map ?
    Mis à part l’approche que tu cites juste avant, oui. C’est la seule solution absolument portable je pense. Mais c’est moins efficace en effet.

  18. #18
    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
    J'ai modifié mon code, pour utiliser un strdup(). J'obtiens alors le résultat attendu.

    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
    63
    64
    65
    66
    67
    68
    69
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <search.h>
     
    ENTRY e;
     
    ENTRY* add(const char *key)
    {
        puts(__func__);
    #ifdef USE_STRCPY
        strcpy(e.key, key);
    #else
        e.key = strdup(key);
    #endif
        ENTRY *res = hsearch(e, ENTER);
        return res;
    }
     
    ENTRY* search(const char *key)
    {
        puts(__func__);
    #ifdef USE_STRCPY
        strcpy(e.key, key);
    #else
        e.key = strdup(key);
    #endif
        ENTRY *res = hsearch(e, FIND);
        return res;
    }
     
    int main(void)
    {
        hcreate(10);
     
    #ifdef USE_STRCPY
        e.key = malloc(1000);
    #endif
     
        const char *bonjour = "bonjour";
        const char *woke = "woke";
     
        ENTRY *one = add(bonjour);
        ENTRY *two = add(woke);
        ENTRY *three = search(bonjour);
     
        printf("ENTRY @ %p\n", e.key);
     
        if(one)
            printf("%p %s\n", one->key, one->key);
        else
            puts("NULL");
     
        if(two)
            printf("%p %s\n", two->key, two->key);
        else
            puts("NULL");
     
        if(three)
            printf("%p %s\n", three->key, three->key);
        else
            puts("NULL");
     
    #ifdef USE_STRCPY
        free(e.key);
    #endif
        //hdestroy();
        return 0;
    }
    Puis :
    $ gccw test.c && ./a.out && gccw -DUSE_STRCPY test.c && ./a.out 
    add
    add
    search
    ENTRY @ 0x10c2009e0
    0x10c200980 bonjour
    0x10c2009b0 woke
    0x10c200980 bonjour
    add
    add
    search
    ENTRY @ 0x109700980
    0x109700980 bonjour
    0x109700980 bonjour
    0x109700980 bonjour
    Ce qui me fait vraiment pencher pour l'hypothèse évoquée précédemment.

Discussions similaires

  1. Ranger un tableau de String dans ma HashMap
    Par jeyce dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 26/08/2004, 22h41
  2. [HashMap] Modification d'objet d'une Hashtable
    Par viena dans le forum Collection et Stream
    Réponses: 6
    Dernier message: 29/07/2004, 09h04
  3. Surcharge de l'égalité et HashMap
    Par olivierM dans le forum Collection et Stream
    Réponses: 13
    Dernier message: 10/06/2004, 09h54
  4. [débutant] cherche a copier une HashMap
    Par mathieublanc13 dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 09/05/2004, 14h33
  5. question sur les variables globales et les thread posix
    Par souris_sonic dans le forum POSIX
    Réponses: 5
    Dernier message: 13/06/2003, 13h59

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