Aller au contenu

Discussion:Programmation C/Types avancés

Le contenu de la page n’est pas pris en charge dans d’autres langues.
Ajouter un sujet
Un livre de Wikilivres.
Dernier commentaire : il y a 17 ans par Tpierron

Alignement et bourrage (padding)

[modifier le wikicode]

J'ai supprimé l'exemple suivant de code pour offsetof:

long distance = (long) &((struct ma_structure *)NULL)->champ;

Mis à part le fait que le type est size_t (au moins dans le n1124), il utilise une conversion d'une valeur de type pointeur vers un type entier, ce qui n'est pas portable. J'ai ensuite rapidement pensé à

static ma_structure s;

size_t distance = &s.champ - &s;

Mais les types sont incompatibles. Deux casts vers (void*) ne résolvent pas encore les choses, car la soustraction de pointeurs ne se fait que pour des pointeurs sur objets (void* est donc exclu). Caster en (unsigned char*)(void*) serait donc encore mieux, mais il reste la contrainte qu'on ne peut soustraire que dans un tableau... Ne sachant plus à quel point il est portable de traiter une structure comme un tableau de unsigned char, et donc si cette soustraction serait définie par la norme, j'ai préféré laisser tomber l'exemple. Si une référence exacte de la norme permet de le valider, alors on peut le mettre dans le texte. Alveric 21 juillet 2006 à 11:57 (CEST)Répondre

Mouais, ce n'est pas parce que ce n'est pas écrit noir sur blanc dans la norme, que ce n'est pas forcément portable. Intuitivement, on voit bien que ce genre d'expression fait le boulot qu'on lui demande. Je ne prendrais pas trop de risque en disant que 100% des implémentations d'offsetof utilise une astuce de ce genre. J'ai beau me creuser les méninges, je ne vois vraiment pas ce qui pourrait faire foirer cette expression, et crois moi que s'il y en avait ne serait-ce qu'une, même infime, je ne l'aurais pas mise. Ah, ouais et si possible une raison autre que c'est_pas_dans_la_norme™, parce qu'à ce petit jeu là on peut enculer les mouches très profond. Peut-être avec une archie qui utilise une segmentation de la mémoire style 286, et encore j'en doute. Thierry Pierron 21 juillet 2006 à 16:10 (CEST)Répondre
Je suis d'accord que le nombre d'implémentations où le code foirerait doit être proche de 0... J'ai vérifié dans le Rationale du C89 (page 151 du pdf):
The offsetof macro was added to provide a portable means of determining the offset, in bytes, of a member within its structure. This capability is useful in programs, such as are typical in database implementations, which declare a large number of different data structures: it is desirable to provide "generic" routines that work from descriptions of the structures, rather than from the structure declarations themselves.
In many implementations, offsetof could be defined as one of
(size_t)&(((s_name*)0)->m_name)
or
(size_t)(char*)&(((s_name*)0)->m_name)
or, where X is some predeclared address (or 0) and A(Z) is defined as ((char*)&Z),
(size_t)(A((s_name*)X->m_name ) - A(X))
It was not feasible, however, to mandate any single one of these forms as a construct guaranteed to be portable. Some implementations may choose to expand this macro as a call to a built-in function that interrogates the translator’s symbol table.
On est donc d'accord, et le Rationale le dit bien, many implementations mais pas guaranteed to be portable. Je vais quand même remettre l'exemple, car il peut (peut-être) permettre une meilleure compréhension, mais en précisant que offsetof est la seule manière strictement portable de le faire. Alveric 26 juillet 2006 à 12:18 (CEST)Répondre
Moui, j'ai juste un peu retravaillé le texte pour que l'enchainement soit plus naturel. Sinon j'ai traduit "bytes" par "mot machine". Désolé ça me fait trop mal aux yeux de voir ce mot anglais.Thierry Pierron 26 juillet 2006 à 15:53 (CEST)Répondre
Ok pour tes modifs, à part que je ne trouve pas offsetof si contraignante que ça... Mais bon, c'est subjectif. Alveric 26 juillet 2006 à 16:43 (CEST)Répondre
Bah, le problème avec cette macro, c'est que tu ne peux pas lui transmettre un type pointeur. Pour peu que tu fasses une définition de ce genre :
typedef struct { /* bla bla */ } * mon_type;
Pouf, la macro est inutilisable.Thierry Pierron 26 juillet 2006 à 17:42 (CEST)Répondre


Okay, j'ai encore charcutée cette section qui était devenu imbitable, à force de rappel, contre-rappel, précision, remarques, note, etc ... Bon le but de cette section ce n'est pas d'expliquer la vie, l'univers et le tout le reste mais simplement de faire remarquer que : les champs des structs ne se suivent pas en mémoire, il y a des octets de bourrages, et d'illustrer par un exemple proche (mais pas forcément exact) de ce qui se passe dans la réalité.

Rappeler que les char peuvent faire plus que 8bits, on le sait, on l'a mattraqué dans la section relatif aux types de bases. Dans cet exemple, char = 8bits. Point barre.

Dans la réalité il y a tellement de cas de figures qu'essayer d'en effleurer les possibilités rendrait le texte complètement imbitable (on n'en était pas là, mais on suivait ce chemin). Thierry Pierron 25 juillet 2007 à 22:13 (CEST)Répondre

Bon je suis un type genre à l'ouest mais ce qu'il faut comprendre (et ce qui est important) dans le padding c'est lorsque l'on touche à la notion objet: en effet si on fait pas gaffe et que notre structure n'est pas aligné le compilateur va rajouter des blanc et donc ce qui se passe ici deviendra aléatoire: fr.wikibooks.org/wiki/Discussion:Programmation_C/Pointeurs (vers la fin sur la notion d'objet)(euh... pas dans l'exemple mais faut comprendre dsl j'ai pas mieux sous la main)

Unions

[modifier le wikicode]

Le code suivant, donné en exemple, utilise des structures et une union anonymes:

struct {
        int type;
        union {
           struct {
              struct point *p1, *p2;
            };
           struct {
              struct point *p;
              struct droite *d;
            };
        };
} droite;

Or il me semble que ces constructions ne sont pas valides en C (GCC ne les aime ni en mode C90, ni en mode C99), seul le C++ les définit, et certains compilateurs C en tant qu'extension (dont GCC en mode "gnu89" et "gnu99" et quelques uns que j'ai trouvés sur google). Quelqu'un pour confirmer ? Alveric 26 juillet 2006 à 12:18 (CEST)Répondre

Arf, exact. En plus l'exemple est vraiment pourri, il ne montre même pas comment on s'en sert (accès aux champs). Même les struct anonymes me paraissent

douteuses. Je vais changer ça. Thierry Pierron 26 juillet 2006 à 15:30 (CEST)Répondre

Je pense que la séquence précédente est plus simple si on prend les chose comme ça:
on oublie le faite que plein de chose sont imbriquée, ce qui offre juste un espace, c'est à dire:
un "truc" ou on peut lire si on veut lire et écrire si on veut écrire.
C'est une cause de segfault: invalid read, invalid right, region not mapped...C'est ce qui rend ce chose un peut compliquer
voici un autre exemple tout aussi tordu.

struct lstr{
  char *str;
  size_t s;
};
struct deuxchar{
 char c[2];
 short int ____;
 int _pad1_;
 size_t _pad2_
 char enfait_le deuxieme_ira_ici;
 char pad[7];
 size_t fin_espace;
}deuxchar;
union altern{
 struct lstr dstr[2]; /*je manage des chaines qui peuvent alterner*/
 struct lstr str;
 struct deuxchar c;
 char (*data)[4]; /*alors la notation exacte je ne sais pas, désolé :/*//*representable: |char*|size_t|char *|size_t|*/
}altern;

mon lstr represente une long string, une chaine de caractere trop longue pour que ça soit prévue. Et le sstr une short string, quelque chose qui tiens sur sizeof(char *) car ce que je veux, disons que sur certaine condition je sais que j'attend pas de '\0'.
En effet printf par exemple attend ce '\0' si non il va dans un espace ou il ne peux pas lire les données.
mais voila un premier probleme:
c'est que moi je veux que mon PC fasse le plus possible de récursif, on va automatiser les accées
la c'est compliqué et je peux pas trop l'expliquer pour l'instant:
ce que je suppose c'est q'on veut tanter de garder les données "alignées", entre guillemet car c'est pas vrais, en revanche des calcules simple qui permettront de calculer des déplacements. On ira chercher les datas dans l'espace avec l'adresse (réelle, l'objet left value, le &) souhaité.Au lieux de chercher dans un tableau de char un offset du tableau, on ira aux address à partir de l'adresse d'une case.Ce sont des déplacement dont on entant beaucoup parler en Assembleur car le C permet faire ces 2 types de déplacement (mais je sais plus commant ça s'appelle). Ce qui donne des trucs barbare comme ça:

union altern a = {{{"c",1},{"b",1}}};
union altern b = {{{"arbre",5},{"feuille",7}}};
((void **)&a.data)[2] = (void *)&((char *)&((void **)&a->data)[0])[1];
/*de cette maniere:*/
printf ("%c", *(char *)((void **)&a.data)[0]);
printf ("%c", *(char *)((void **)&a.data)[2]);
printf ("%c", *(char *)((void **)&b.data)[0]);
printf ("%c", *(char *)((void **)&b.data)[2]);
/*et par exemple:*/
printf ("%lu,%lu", ((size_t *)&b.data)[1],((size_t *)&b.data)[3]);

tout en informant d'éviter de faire comme ça

struct mon_union{
 char (*c)[2]
};
mon_union.c[0] = *pourrit_1
mon_union.c[1] = *pourrit_2

mais comme tu le dis sans réel mode d'emploi on peut pas deviner. Ici, si tu retires les structure, rien n'informe sur le "mode d'emploi" de ton espace. car j'aurais tout simplement pu definir ça:

union{
size_t data[4]
}

car dans le fond j'ai besoin de 2 size_t et 2 void *: 2*sizeof(size_t)+2*sizeof(void *), ou même 4*8*sizeof(char).

Mais le plus interéssant c'est qu'à la fin voila ce que vous pouvez trouver:

#define DATA_STR(s,n) (char *)((void **)&s)[n]
#define DATA_SZ(s,n) ((size_t *)&s)[n+1]
printf ("%s:%lu\n",DATA_STR(b.data,0),DATA_SZ(b.data,0))