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 :

Allocation de mémoire pour des données statiques adjacentes.


Sujet :

C

  1. #1
    Membre actif

    Femme Profil pro
    Étudiant
    Inscrit en
    Novembre 2013
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2013
    Messages : 87
    Points : 217
    Points
    217
    Par défaut Allocation de mémoire pour des données statiques adjacentes.
    Bonjour à tous,

    Quand je fait un cast d'une donnée var1 d'une taille de 4 octets à un type structuré d'une taille plus grande (15 octets), je pense que la mémoire qu’occuperont les nouveaux champs sera celle des données adjacentes à var1 (donnée définie juste avant ou après var1).
    Pour m'assurer que mon raisonnement est correcte j'ai fait l'exemple suivant; et il s'est avéré que j'ai tort!
    Car j'ai initialisé les deux tableaux tab1 et tab2 à 0xFF mais quand j'affiche le contenu de la donnée après cast: je ne trouve que des 0!
    Ma question est la suivante: comment les données sont elles gérées/allouées en mémoire. est ce que tab1, var1 et tab2 ,dans cet ordre, ne sont elles pas rangées dans la section .bss l'une à coté de l'autre ?

    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    typedef struct{
     unsigned char M[10];
     unsigned char number;
     float note;
    }my_type;
     
    unsigned char tab1[40];
    unsigned int var1;
    unsigned char tab2[40];
     
    my_type *ptr_to_var1;
     
    int main(void) {
    int i;
     
        for(i=0;i<40;i++)
        { tab1[i]=0xFF;}
     
        for(i=0;i<40;i++)
        { tab2[i]=0xFF;}
     
        var1 = 0xFFEE77AA;
     
        ptr_to_var1 =(my_type*)&var1;
     
        for(i=0;i<10;i++)
        {printf("%d\n",ptr_to_var1->M[i]);}
        printf(" %f\n %d\n",ptr_to_var1->note,ptr_to_var1->number);
     
        system("pause");
    }
    Merci d'avance pour vos réponses et vos explications!

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 387
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 387
    Points : 23 702
    Points
    23 702
    Par défaut
    Bonjour,

    C'est dû au fait que tu as utilisé des variables globales. Déporte ces trois variables à l'intérieur de ta fonction main et le résultat sera tout autre.

    Contrairement aux variables locales qui sont dynamiques, c'est-à-dire allouées à la volée dans la pile et dont l'état initial est indéfini (correspondant en fait à l'état dans lequel est la mémoire à l'emplacement qu'on leur a alloué), le C garantit que les variables globales et les statiques doivent être initialisées à zéro en début d'exécution. De plus, sauf indications contraires, les globales sont visibles « de l'extérieur », comme les fonctions, et leur nom apparaît donc dans la table des symboles du fichier exécutable généré.

    Toutes ces variables prennent donc place dans une section spécifique appelée « BSS » et qui, elle, peut être organisée n'importe comment par le compilateur en fonction de ce qu'il a à y mettre (on y retrouve pas mal de choses). Par la suite, il y a des chances pour que le fichier soit directement mappé en mémoire plutôt que lu, interprété et reconstruit. Donc, tes variables prendront directement la place qu'elles ont dans le fichier ou l'offset qui y est précisé.

  3. #3
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Il initialise déjà ses tableaux, je ne pense pas que cela change grand chose à son problème. Non c'est surtout que ton exemple est incorrect o.meryem et que tu as au moins deux sources de comportement indéfini ligne 28 :

    • tu déréférences un pointeur vers type donné (int) après l'avoir casté en un pointeur vers un type incompatible (my_type) : c'est illégal (strict aliasing) ;
    • my_type est de taille supérieure à int et tu n'as même pas alloué l'espace supplémentaire que cela nécessiterait : je ne te fais pas de dessin...


    Tu n'as aucune garantie que ton compilateur alloue tes variables bien gentiment à la queue-leu-leu, collé-serré. Tu peux obtenir une partie de cette garantie en les plaçant à l'intérieur d'une structure, mais : il existe également une contrainte héritée de la machine appelée alignement qui s'applique à tout objet adressable (dont les variables globales ainsi que les structures et leurs membres). La valeur d'alignement à l'intérieur d'une structure correspond à la taille en octets du plus grand de ses membres (d'où sizeof(my_type) == 16 et non 15 comme tu le pensais).

    TL;DR : le compilateur s'assure que l'adresse d'une variable d'un type donné est un multiple de la taille de ce type. L'adresse d'un entier 64-bit par exemple est forcément divisible par 8. Selon la définition d'une structure, on peut donc avoir des zones mémoire non-utilisées entre les membres (padding) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    struct {
        char     a[3]; // &a - &foo ==  0
        double   b;    // &b - &foo ==  8 (padding de 5 octets)
        uint32_t c;    // &c - &foo == 16 (pas de padding)
        char     d;    // &d - &foo == 20 (pas de padding)
        uint64_t e;    // &e - &foo == 24 (padding de 3 octets)
    } foo; // sizeof(foo) == 32 octets (et non pas 24)

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 387
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 387
    Points : 23 702
    Points
    23 702
    Par défaut
    Bonsoir,

    Citation Envoyé par Matt_Houston Voir le message
    Il initialise déjà ses tableaux, je ne pense pas que cela change grand chose à son problème. Non c'est surtout que ton exemple est incorrect o.meryem et que tu as au moins deux sources de comportement indéfini ligne 28 :
    Merci pour cette contribution mais tu as posté un peu vite (et un peu trop tard aussi, probablement).
    Le problème ne vient pas de l'initialisation ou non de ses tableaux, mais du fait que puisqu'il s'agit de variables globales, ils sont distribués différemment en mémoire et dans une autre section.

    • tu déréférences un pointeur vers type donné (int) après l'avoir casté en un pointeur vers un type incompatible (my_type) : c'est illégal (strict aliasing) ;
    • my_type est de taille supérieure à int et tu n'as même pas alloué l'espace supplémentaire que cela nécessiterait : je ne te fais pas de dessin...
    Elle le fait exprès.

    Tu n'as aucune garantie que ton compilateur alloue tes variables bien gentiment à la queue-leu-leu, collé-serré. Tu peux obtenir une partie de cette garantie en les plaçant à l'intérieur d'une structure, mais : il existe également une contrainte héritée de la machine appelée alignement qui s'applique à tout objet adressable (dont les variables globales ainsi que les structures et leurs membres). La valeur d'alignement à l'intérieur d'une structure correspond à la taille en octets du plus grand de ses membres
    Relis bien cette dernière phrase…

    (d'où sizeof(my_type) == 16 et non 15 comme tu le pensais).
    C'est pourquoi elle utilise des tableaux de 40 caractères de part et d'autre, et un tableau de 10 caractères censé être largement supérieur au int transtypé.

    TL;DR : le compilateur s'assure que l'adresse d'une variable d'un type donné est un multiple de la taille de ce type. L'adresse d'un entier 64-bit par exemple est forcément divisible par 8. Selon la définition d'une structure, on peut donc avoir des zones mémoire non-utilisées entre les membres (padding) :
    Absolument pas. Sa taille oui, mais pas son adresse, qui généralement reste alignée sur la taille des mots machine, soit généralement 4 ou 8. Un long long de 8 octets sera toujours aligné sur une adresse multiple de 4 sur une machine 32 bits, lequel multiple n'est pas forcément multiple de 8 également (il a une chance sur deux de l'être). En plus, si l'on suivait ce raisonnement, cela voudrait dire qu'un tableau de neuf caractères char tab[9], pour lequel sizeof tab renvoie toujours 9, devrait être aligné sur des adresses multiples de 9 octets, ce qui commencerait à être vraiment pénalisant pour la machine.

    Par exemple, le programme suivant compilé en 32 bits avec GCC sous Linux :

    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
    #include <stdio.h>
     
    int main (void)
    {
        struct
        {
            char a[9];
            long long b;
            char c;
        } s;
     
        printf ("a: %u %p\n",(unsigned int)sizeof s.a,(void *)&(s.a));
        printf ("b: %u %p\n",(unsigned int)sizeof s.b,(void *)&(s.b));
        printf ("c: %u %p\n",(unsigned int)sizeof s.c,(void *)&(s.c));
     
        return 0;
    }
    … donne le résultat suivant :

    Code Shell : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $ gcc -m32 -std=c99 programme.c -o programme
    $ ./programme
    a: 9 0xfffd5ff8
    b: 8 0xfffd6004
    c: 1 0xfffd600c

    … soit un long long de taille 8 aligné sur une adresse multiple de 4.

  5. #5
    Expert confirmé
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Points : 4 182
    Points
    4 182
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    Merci pour cette contribution mais tu as posté un peu vite (et un peu trop tard aussi, probablement).
    Le problème ne vient pas de l'initialisation ou non de ses tableaux, mais du fait que puisqu'il s'agit de variables globales, ils sont distribués différemment en mémoire et dans une autre section.
    Jusqu'ici nous sommes d'accord, mais :

    Citation Envoyé par Obsidian Voir le message
    Elle le fait exprès.

    C'est pourquoi elle utilise des tableaux de 40 caractères de part et d'autre, et un tableau de 10 caractères censé être largement supérieur au int transtypé.
    J'ai bien compris ce que l'OP cherchait à faire, mais c'est malheureusement illégal. Le comportement résultant de l'exécution de ce code est donc indéfini et on peut faire tous les tests que l'on désire, les résultats n'auront aucune valeur puisqu'ils ne permettront pas d'extrapoler le fonctionnement général du système. Cela aurait plus de sens d'étudier les asm résultant de différentes configurations.


    Citation Envoyé par Obsidian Voir le message
    Absolument pas. Sa taille oui, mais pas son adresse, qui généralement reste alignée sur la taille des mots machine, soit généralement 4 ou 8. Un long long de 8 octets sera toujours aligné sur une adresse multiple de 4 sur une machine 32 bits, lequel multiple n'est pas forcément multiple de 8 également (il a une chance sur deux de l'être).
    Oui tu as parfaitement raison, j'ai tapé ça trop vite.

    Citation Envoyé par Obsidian Voir le message
    En plus, si l'on suivait ce raisonnement, cela voudrait dire qu'un tableau de neuf caractères char tab[9], pour lequel sizeof tab renvoie toujours 9, devrait être aligné sur des adresses multiples de 9 octets, ce qui commencerait à être vraiment pénalisant pour la machine.
    Il s'agissait bien évidemment du type adressable de base, soit ici char et non char[9]. Mais tu as raison de pointer les inexactitudes de mon propos. Pour la peine je m'impose quelques heures de Java..

  6. #6
    Membre actif

    Femme Profil pro
    Étudiant
    Inscrit en
    Novembre 2013
    Messages
    87
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2013
    Messages : 87
    Points : 217
    Points
    217
    Par défaut
    Bonjour,

    Merci pour vos réponses!

    En fait, comme l'a bien dit Obsidian je le fait exprès de créer cette incompatibilité entre les deux données. Histoire de savoir et comprendre ce qui se passera!

    J'ai betement pensé que les variables globales seront mappées dans l'ordre de leurs définitions dans les fichiers .H/.C et donc je saurais voir l'effet de ce comportement.

    Mais je comprends, grace à vos explications, que c'est un comportement indéfini et on peut pas savoir d'où viennent les 0 que je trouve dans les autres champs de la structure my_type!

    cordialement,

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

Discussions similaires

  1. Réponses: 19
    Dernier message: 06/02/2014, 19h30
  2. Gestion mémoire des données statiques
    Par oodini dans le forum C++
    Réponses: 11
    Dernier message: 20/03/2009, 15h42
  3. Réponses: 11
    Dernier message: 16/07/2007, 16h33
  4. [XML] Des données statiques
    Par buchir dans le forum Bibliothèques et frameworks
    Réponses: 1
    Dernier message: 13/07/2007, 16h19
  5. Réponses: 1
    Dernier message: 24/10/2006, 00h24

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