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 :

Structures et alignement de données


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut Structures et alignement de données
    Bonjour,

    Dans un projet, des consignes nous imposent de "masquer l'implémentation des structures".

    Mon binôme est arrivé à une structure de ce genre (on utilise des pointeurs de fonctions pour des raisons bien particulières) :

    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
    typedef struct ImplEnsembleColore {
      TYPE * (* next)(GoshIterateur *, struct ImplEnsembleColore *, TYPE *);
      GoshIterateur(*createIterateur)(void);
      bool (*vide)(struct ImplEnsembleColore *);
      void (*ajouter)(struct ImplEnsembleColore *, TYPE);
      bool (*appartient)(struct ImplEnsembleColore *, TYPE);
     
    #ifdef SHOW_IMPLEMENTATION_ENSEMBLE_COLORE
     
     EnsemblePosition positions;
     Couleur couleur;
     
    #endif
     
    } * EnsembleColore;
    Dans le .c, SHOW_IMPLEMENTATION_ENSEMBLE_COLORE sera défini et on pourra accéder à tous les champs.

    Ailleurs, on ne pourra récupérer que les pointeurs de fonctions (les autres champs n'étant pas définis) de cette manière (EnsembleColore ne sera d'ailleurs jamais déférencé) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    EnsembleColore foo;
    // ....
    foo->unDesPointeurs();
    A mon grand étonnement, ce code compile ( j'étais pourtant sûr que l'éditeur de liens proférerait quelques insultes) mais je me demande s'il ne peut pas y avoir des problèmes avec "l'alignement".
    En effet, dans une structure, tous les éléments sont dans l'ordre en mémoire mais pas forcément consécutif, le compilateur pouvant rajouter des octets de padding pour des raisons d'optimisations.

    J'aimerai donc savoir, même si ce code fonctionne, si le comportement est bien défini par la norme.

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Chercheur d'emploi
    Inscrit en
    Septembre 2007
    Messages
    7 477
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Chercheur d'emploi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 477
    Par défaut
    Hello,

    On ne le dira jamais assez : cacher un pointeur dans un typedef, c'est mal. Le langage C n'est pas le Java et on ne peut pas simuler efficacement le comportement de l'un avec l'autre.

    À part cela, je ne comprends pas vraiment ce qui te pose problème. L'alignement et le padding n'entrent pas en ligne de compte si tu accèdes aux membres d'une structure par leur nom (et il n'y a pas de raison de le faire autrement) et, dans ton cas, tes pointeurs de fonctions sont en dehors de la compilation conditionnelle, donc toujours visibles, comme tu nous l'expliques. Je ne vois pas pourquoi l'éditeur de liens trouverait à y redire.

    EN REVANCHE, ta compilation conditionnelle semble être une façon maladroite d'implémenter la notion de membres privés mais, malheureusement, toi et ton binôme ne peuvent absolument pas procéder de cette façon car la longueur de votre structure sera différente d'une unité de compilation à l'autre. Si une routine de votre programme renvoie un tableau de structure et que vous l'indexez à partir d'une autre unité, vous allez vous retrouver à cheval sur deux éléments consécutifs en mémoire et faire face à une très grosse corruption de données.

  3. #3
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Citation Envoyé par Obsidian Voir le message
    On ne le dira jamais assez : cacher un pointeur dans un typedef, c'est mal. Le langage C n'est pas le Java et on ne peut pas simuler efficacement le comportement de l'un avec l'autre.
    On sait, ne remue pas le couteau dans la plaie
    Consigne du prof

    Je ne vois pas pourquoi l'éditeur de liens trouverait à y redire.
    Comme on déclare deux structures de même nom avec un contenu différent, je pensais qu'il crierait au blasphème.

    L'alignement et le padding n'entrent pas en ligne de compte si tu accèdes aux membres d'une structure par leur nom (et il n'y a pas de raison de le faire autrement) et, dans ton cas, tes pointeurs de fonctions sont en dehors de la compilation conditionnelle, donc toujours visibles, comme tu nous l'expliques.
    Mais le problème, c'est qu'on a au final deux structures avec un contenu différent : A et B.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    A a;
    a->pointeurFonction = foo;
    //....
     
    B b = a;
    b->pointeurFonction();
    Est-ce qu'on est sûr à 100% que "pointeurFonction" est à la même place en mémoire dans les deux structures et qu'il n'aurait pas été "décalé" ?


    EN REVANCHE, ta compilation conditionnelle semble être une façon maladroite d'implémenter la notion de membres privés mais, malheureusement, toi et ton binôme ne peuvent absolument pas procéder de cette façon car la longueur de votre structure sera différente d'une unité de compilation à l'autre. Si une routine de votre programme renvoie un tableau de structure et que vous l'indexez à partir d'une autre unité, vous allez vous retrouver à cheval sur deux éléments consécutifs en mémoire et faire face à une très grosse corruption de données.
    On instancie, détruit et manipule les instances que dans le .c où SHOW_IMPLEMENTATION_ENSEMBLE_COLORE est défini.
    Ailleurs, on ne manipule que des pointeurs.
    Donc ça devrait être "bon" ?

  4. #4
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    On instancie, détruit et manipule les instances que dans le .c où SHOW_IMPLEMENTATION_ENSEMBLE_COLORE est défini.
    Ailleurs, on ne manipule que des pointeurs.
    Donc ça devrait être "bon" ?
    Pour moi, ça signifie que ton mode opératoire n'est pas robuste. C'est trop compliqué d'expliquer à l'utilisateur de ton module "alors, il faut bien définit la macro et faire attention à ne pas mixer avec ce qui vient d'un fichier où elle ne serait pas définie". Trop de risque, trop de problème.

    Ne serait-il pas mieux de créer une type opaque et de l'utiliser à la place de ce qui est entre la compilation conditionnelle (qu'on vire au passage) ? Ainsi, tu aurais la même structure partout, mais c'est seulement à l'endroit où le type opaque sera défini que tu pourras faire des opérations sur "l'implémentation" ?
    http://en.wikipedia.org/wiki/Opaque_pointer
    http://en.wikipedia.org/wiki/Opaque_data_type

    Pour ce qui est du padding et donc de l'alignement, je suis comme Obsidian : je m'inquiète surtout par des contenus non compatibles des structures.
    Est-ce qu'on est sûr à 100% que "pointeurFonction" est à la même place en mémoire dans les deux structures et qu'il n'aurait pas été "décalé" ?
    Je pense que quand tu fais ça, le padding et l'alignement ne rentre pas en compte. Tu accèdes à un champ sans savoir où il est dans la structure.


    PS : je viens de faire un test avec des typedef avec le même alias mais pas les mêmes types réels.

    Code fichier1.c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    typedef const char* TYPE;
     
    /*extern*/ void print(TYPE t);
     
    int main (void)
    {
        TYPE t = "bonjour le monde";
        print(t);
        return 0;
    }

    Code fichier2.c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    typedef long TYPE;
     
    void print(TYPE t)
    {
        printf("%d %X %s\n", t, t, t);
    }
    Le compilateur est d'accord. Je crois que je vais enquêter dans la norme à ce sujet ^^

  5. #5
    Inactif  


    Homme Profil pro
    Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Inscrit en
    Décembre 2011
    Messages
    9 026
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 32
    Localisation : France, Loire (Rhône Alpes)

    Informations professionnelles :
    Activité : Doctorant sécurité informatique — Diplômé master Droit/Économie/Gestion
    Secteur : Enseignement

    Informations forums :
    Inscription : Décembre 2011
    Messages : 9 026
    Par défaut
    Citation Envoyé par Bktero Voir le message
    Ne serait-il pas mieux de créer une type opaque et de l'utiliser à la place de ce qui est entre la compilation conditionnelle (qu'on vire au passage) ? Ainsi, tu aurais la même structure partout, mais c'est seulement à l'endroit où le type opaque sera défini que tu pourras faire des opérations sur "l'implémentation" ?
    http://en.wikipedia.org/wiki/Opaque_pointer
    http://en.wikipedia.org/wiki/Opaque_data_type
    Le problème c'est que j'ai besoin d'accéder aux pointeurs sur fonctions à l'extérieur du .c


    Je pense que quand tu fais ça, le padding et l'alignement ne rentre pas en compte. Tu accèdes à un champ sans savoir où il est dans la structure.
    Mais ne faut-il pas que les deux champs soient au "même endroit" dans les deux structures ?
    Si dans struct A il est en 0x04
    Et dans struct B en 0x00
    Faire a->foo = foo; puis b->foo(); n'appellera pas la bonne fonction non?

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Neckara Voir le message
    Le problème c'est que j'ai besoin d'accéder aux pointeurs sur fonctions à l'extérieur du .c
    Je pensais en fait à :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    typedef struct
    {
        // pointeurs sur fonction
        void (*f)(void);
     
        // le type opaque;
        OPAQUE *p;
    } ElementColore;

    Citation Envoyé par Neckara Voir le message
    Mais ne faut-il pas que les deux champs soient au "même endroit" dans les deux structures ?
    Si dans struct A il est en 0x04
    Et dans struct B en 0x00
    Faire a->foo = foo; puis b->foo(); n'appellera pas la bonne fonction non?
    Ce que tu mets en évidence, c'est bien que tes structures ne sont pas identiques. Et si elles ne sont pas identiques, elles sont sûrement incompatible. Je pense que cela dépend de la taille des éléments manquants. Selon si leur taille est inférieure ou supérieure à celle d'un pointeur de fonction, l'alignement peut changer. Je ne sais pas si l'appel fonctionnera, il faudrait tester.

    PS :
    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
    #include <stdio.h>
     
    typedef struct
    {
        void (*f)(void);
    } TYPE;
     
    /*extern*/ void print(TYPE t);
    /*extern*/ TYPE get(void);
     
    void aFunction(void)
    {
        printf("I am %s()\n", __func__);
    }
     
    int main (void)
    {
        printf("aFunction = %p\n", aFunction);
     
        TYPE here = {aFunction};
        print(here);
     
        TYPE stranger = get();
     
        // Les lignes suivantes generent des erreurs a l'execution
        print(stranger);
        stranger.f = aFunction;
        print(stranger);
        stranger.f();
     
        return 0;
    }
    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
    #include <stdio.h>
    typedef struct
    {
        double number;
        void (*f)(void);
    } TYPE;
     
    void print(TYPE t)
    {
        puts("** TYPE **");
        printf("number = %ld\n", t.number);
        printf("f() = %p\n", t.f);
        puts("** **** **");
    }
     
    TYPE get(void)
    {
        return (TYPE) {666, NULL};
    }
    Pas de message à la compilation, mais ça se vautre à l’exécution.

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

Discussions similaires

  1. Structure de base de donnée (optimisation?)
    Par juJuv51 dans le forum SQL Procédural
    Réponses: 5
    Dernier message: 23/02/2007, 21h05
  2. Structure de base de données
    Par hphil dans le forum SQL Procédural
    Réponses: 4
    Dernier message: 19/07/2006, 20h45
  3. Réponses: 8
    Dernier message: 05/12/2005, 12h52
  4. Réponses: 4
    Dernier message: 17/02/2004, 08h36
  5. structure des bases de données Palm
    Par nomdutilisateur dans le forum Bases de données
    Réponses: 2
    Dernier message: 17/01/2004, 17h47

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