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 :

Subtilités sur les unions


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Février 2003
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2003
    Messages : 15
    Par défaut Subtilités sur les unions
    D'apres ce que je comprends du livre "Le langage C" une union peut contenir plusieurs mais seul le dernier champ écrit est valide :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    union {
       int val1;
       float val2;
    } u;
    Dans l'union précédente seule val1 ou val2 est valide à un instant donné. Jusque la je comprends.

    Maintenant je vais donner un cas que je comprends pas... Voici une union (pour ceux qui connaissent (linux/netfilter_ipv6/ip6_tables.h) :

    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
     
    struct ip6t_entry_match
    {
            union {
                    struct {
                            u_int16_t match_size;
     
                            /* Used by userspace */
                            char name[IP6T_FUNCTION_MAXNAMELEN-1];
                            u_int8_t revision;
                    } user;
                    struct {
                            u_int16_t match_size;
     
                            /* Used inside the kernel */
                            struct ip6t_match *match;
                    } kernel;
     
                    /* Total length */
                    u_int16_t match_size;
            } u;
     
            unsigned char data[0];
    };
    Voici un code écrivant dans cette union (adapté de ip6tables.c pour les connaisseurs aussi ):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    struct ip6t_entry_match m;
    m.u.match_size = size;
    strcpy(m.u.user.name, name);
    set_revision(m.u.user.name, revision);
    Et la... C'est le drame !
    Ce code ne devrait pas selon ce que j'ai compris faire ce que le programmeur souhaitait faire (marrante cette tournure de phrase ...), à savoir écrire dans la variable match_size de l'union ET dans la structure user de l'union. Selon moi, une fois que l'on a écrit dans la structure user, la valeur contenue dans match_size ne doit plus être valide.

    Or ce n'est pas le cas, la valeur contenue dans match_size est toujours valide et égale à l'affectation précédente (vive printf) !

    Quelqu'un a une explication a me donner pour ça ? (J'ai ma petite idée mais je me la réserve )

    P.S : Si ca se trouve c'est gros comme le nez au milieu de la figure et que je suis fatigué par une journée de relecture de code iptables

  2. #2
    Membre éclairé
    Profil pro
    Inscrit en
    Juin 2005
    Messages
    760
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2005
    Messages : 760
    Par défaut
    L'union peut se voir comme une une structure ou les membres au lieu de se suivre en memoire occupent simultanement la meme memoire. Dans ton cas, en lisant et en considerant que le compilateur ne fait pas des optimisations differentes pour les structures, la premiere partie de ta memoire est reservee pour ton u_int16_t match_size dans les trois cas (user, kernel ou juste ce size).

    Par contre, cela n'est pas garanti. En modifier une, peut laisser une autre intact eventuellement.

    Ce qu'il faut comprendre c'est que du fait de la superposition, ecrire dans un membre de l'union modifie les donnees de l'union, mais il n'y a pas d'"invalidation". Juste que si tu lis un membre, alors que tu a ecris dans un autre, ce premier membre peut voir ses donnees modifiées. (Encore une fois tout depend du "mapping" memoire.)

    Je sais pas ce que c'est ce que tu attendais comme reponse par contre

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Février 2003
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2003
    Messages : 15
    Par défaut
    Ok donc du fait que la première partie de la mémoire est réservée pour le match_size le fait d'écrire dans le champ name de la structure ne modifie pas le contenu de match_size.
    Ceci car la structure contient elle aussi un champ match_size et que la mémoire réservée pour ce champ est la même que le champ match_size de l'union.

    Je pense avoir compris et ma petite idée allait bien dans ce sens mais je n'avais pas tout le raisonnement

    Autre petite question d'ordre général :

    soit une struture (très originale):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    struct toto {
       int tata;
       float titi;
    }
    L'allocation de la mémoire pour cette structure est elle la suivante dans TOUS les cas : le champ tata réserve le début de la mémoire et le champ titi occupe la fin de la mémoire ?

    Au final, l'allocation de la mémoire pour différents champs d'une structure se fait elle toujours du premier qui occupe le début de la mémoire allouée pour la structure complète au dernier champ qui lui occupe la fin.

    Merci bcp pour ton aide en tous les cas Tabris

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

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

    Informations forums :
    Inscription : Mai 2005
    Messages : 3 499
    Par défaut
    Salut,

    Soit le programme test suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include <stdio.h>
    #include <stdlib.h>
     
    struct toto {
        int tata;
        float titi;
    };
     
    int main(void)
    {
        struct toto obj;
     
        printf("La taille de toto est de %u octets\n", sizeof obj);
        printf("L'adresse de obj: %p\n", (void *) &obj);
        printf("L'adresse de obj.tata: %p\n", (void *) &obj.tata);
        printf("L'adresse de obj.titi: %p\n", (void *) &obj.titi);
        printf("La fin de l'espace alloué à obj: %p\n", 
                (void *) ((char*)&obj + sizeof obj));
     
        return EXIT_SUCCESS;
    }
    Il imprime sur le terminal la sortie suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    La taille de toto est de 8 octets
    L'adresse de obj: 0xbf990d80
    L'adresse de obj.tata: 0xbf990d80
    L'adresse de obj.titi: 0xbf990d84
    La fin de l'espace alloué à obj: 0xbf990d88
    Sur ma machine, les entiers de type int et les nombres à virgule flottante de type float sont tous deux codés sur 4 octets.

    Si on modifie la structure toto:
    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
    #include <stdio.h>
    #include <stdlib.h>
     
    struct toto {
        int tata;
        float titi;
        char tutu;
    };
     
    int main(void)
    {
        struct toto obj;
     
        printf("La taille de toto est de %u octets\n", sizeof obj);
        printf("L'adresse de obj: %p\n", (void *) &obj);
        printf("L'adresse de obj.tata: %p\n", (void *) &obj.tata);
        printf("L'adresse de obj.titi: %p\n", (void *) &obj.titi);
        printf("L'adresse de obj.tutu: %p\n", (void *) &obj.tutu);
        printf("La fin de l'espace alloué à obj: %p\n", 
                (void *) ((char*)&obj + sizeof obj));
     
        return EXIT_SUCCESS;
    }
    La sortie sur la console est:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    La taille de toto est de 12 octets
    L'adresse de obj: 0xbfd5c56c
    L'adresse de obj.tata: 0xbfd5c56c
    L'adresse de obj.titi: 0xbfd5c570
    L'adresse de obj.tutu: 0xbfd5c574
    La fin de l'espace alloué à obj: 0xbfd5c578
    L'adresse du premier membre de la structure correspond toujours à l'adresse de la structure (je n'ai pas la norme sous la main maintenant, et je n'ai pas vérifié). En revanche, le dernier membre de la structure n'occupe pas forcément jusqu'au dernier octet de l'espace mémoire alloué à la structure à cause des contraintes d'alignement. Dans le 1er cas présenté, sur mam machine, le dernier octet alloué à l'objet de type struct toto correspond au dernier octet du membre titi. Dans le 2ème cas, le dernier octet de la structure est aligné (précède) une adresse mémoire multiple de 4, et ne correspond pas l'unique octet alloué au membre tutu, de type char.

    Ainsi, pour en revenir à la structure de départ, il me semble que la norme spécifie que le premier membre de la structure est aligné sur le début de la structure (non vérifié). En ce qui concerne la fin de la structure, cela va dépendre du système, et notamment des contraintes d'alignement.

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

    FAQ-Python FAQ-C FAQ-C++

    +

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Février 2003
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2003
    Messages : 15
    Par défaut
    Merci beaucoup pour votre aide à tout les deux, et merci pour l'exemple très édifiant

    Je comprend maintenant parfaitement pourquoi le code présenté dans le premier poste fonctionne. Le fait que l'allocation mémoire des champs d'une structure se fait dans l'ordre dans lequel ils sont déclarés est bon à savoir.

    Merci a vous deux !

Discussions similaires

  1. Probleme sur les unions
    Par stardeus dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 14/02/2013, 17h15
  2. Question métaphysique sur les unions
    Par lapique dans le forum C++
    Réponses: 7
    Dernier message: 29/05/2008, 18h12
  3. Union sur les dates?
    Par doudine dans le forum Langage SQL
    Réponses: 8
    Dernier message: 11/09/2007, 13h48
  4. Subtilité sur les combo box
    Par SuperWeight dans le forum MFC
    Réponses: 2
    Dernier message: 14/06/2007, 11h17
  5. Réponses: 9
    Dernier message: 17/03/2007, 05h49

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