TD — Séance 4 — Structures et unions
Offsets, padding, dumps de structures, unions et champs de bits
Calculer la disposition d’une structure (offsets, padding, taille), lire le dump d’une structure, d’une structure imbriquée et d’un tableau de structures, réinterpréter des octets par une union, coder des champs de bits, et reconnaître les erreurs de gestion des structures allouées. Sauf mention contraire, la machine est un x86-64 little-endian : char 1 octet, short 2, int et float 4, long, double et pointeurs 8 ; l’alignement de chaque type de base est égal à sa taille. Les adresses sont écrites en hexadécimal.
Exercice 1 : calcul du padding
struct P {
char a;
short b;
char c;
int d;
};struct Q {
int a;
char b;
double c;
char d;
};struct R {
char a;
char b;
short c;
char *d;
};struct T {
double a;
char b[5];
int c;
short d;
};Pour chaque structure, compléter le tableau : offset de chaque champ, octets de padding (entre les champs et à la fin), alignement de la structure et
sizeof.Structure abcdpadding alignement sizeofPQRTDessiner la disposition de
struct Tpar lignes de 8 octets, en marquant le padding.Réordonner les champs de
struct Qpour réduire sa taille. Donner les nouveaux offsets, la nouvelle taille, et l’économie en octets et en pourcentage.Combien d’octets occupe un tableau de 1000
struct Q, avant et après la réorganisation ?
Exercice 2 : lire le dump d’une structure
Une variable e de type struct Etudiant est rangée en 0x80 ; on relève le dump de 0x80 à 0x9f.
| Addr | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 |
|---|---|---|---|---|---|---|---|---|
0x80 |
46 |
61 |
74 |
6f |
75 |
00 |
15 |
00 |
0x88 |
10 |
00 |
00 |
00 |
cc |
cc |
cc |
cc |
0x90 |
98 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x98 |
46 |
61 |
74 |
69 |
63 |
6b |
00 |
cc |
struct Etudiant {
char nom[6];
short age;
int note;
char *ville;
};
struct Etudiant *p = &e; /* 0x80 */Codes ASCII utiles : 46 F, 61 a, 63 c, 69 i, 6b k, 6f o, 74 t, 75 u.
- Donner l’offset de chaque champ, les octets de padding et
sizeof(struct Etudiant). Identifier les octets de padding dans le dump. - Donner la chaîne
e.nom. Que liraitprintf("%s", e.nom)sinomcontenait 6 lettres sans terminateur ? - Donner
e.ageete.note, en hexadécimal et en décimal. - Donner
p->ville, la chaîne qu’il désigne et sa longueur. - Donner
p->ville[3],&p->noteet(char *)&p->ville - (char *)p.
Exercice 3 : structures imbriquées et tableau de structures
| Addr | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 |
|---|---|---|---|---|---|---|---|---|
0x200 |
65 |
00 |
00 |
00 |
0f |
04 |
ea |
07 |
0x208 |
66 |
00 |
00 |
00 |
03 |
07 |
e9 |
07 |
0x210 |
fa |
00 |
00 |
00 |
1c |
02 |
e8 |
07 |
struct Date {
char jour;
char mois;
short annee;
};
struct Evt {
int code;
struct Date d;
};
struct Evt e[3]; /* en 0x200 */
struct Evt *p = e + 1;- Donner
sizeof(struct Date)etsizeof(struct Evt), puis l’offset dedet celui ded.anneedansstruct Evt, tels que les donneoffsetof. - Reconstruire les trois éléments de
e(code, puis la datejour/mois/annee). - Donner
p,p->d.anneeet(p + 1)->code. - Donner l’adresse
&e[2].d.moiset l’expression qui l’obtient à partir depsans crochets. - Donner
(char *)&e[2] - (char *)pet&e[2] - p. Pourquoi diffèrent-ils ?
Exercice 4 : unions
union U {
unsigned int u;
float f;
unsigned char o[4];
};
union U x; /* en 0x60 */
x.f = -2.5f;Donner
sizeof(union U)et l’offset de chacun des trois membres.Écrire le motif IEEE 754 de \(-2{,}5\) (signe, exposant, mantisse), puis
x.uen hexadécimal.Remplir le dump de
xet donnerx.o[0]àx.o[3].Addr +0 +1 +2 +3 0x60Après
x.o[3] = 0x40;, donnerx.uetx.f. Quel bit a changé ?On exécute
union { unsigned short s; unsigned char c[2]; } t; t.s = 0x0102;. Dessiner les deux octets detsur une machine little-endian, puis big-endian, et en déduire une fonctionint est_little_endian(void).
Exercice 5 : champs de bits
struct Status {
unsigned int actif : 1;
unsigned int mode : 2;
unsigned int prio : 3;
unsigned int res : 2;
};
struct Status s = {1, 3, 4, 2};struct Bits2 {
unsigned int a : 3;
unsigned int : 0;
unsigned int b : 5;
unsigned int c : 6;
};Avec gcc sur x86-64, le premier champ occupe les bits de poids faible de l’unité.
- Donner
sizeof s. - Écrire l’octet 0 de
sen binaire (bit 7 à gauche), en marquant chaque champ, puis en hexadécimal. - Après
s.prio = 11;, donner la valeur des.prioet le nouvel octet 0. - Donner
sizeof(struct Bits2)et expliquer l’effet deunsigned int : 0;. Donner les 8 octets d’unestruct Bits2mise à zéro, puis remplie aveca = 5,b = 17,c = 33.
Exercice 6 : structures et allocation
Pour chaque fragment, dire ce qui est incorrect, ce qui se passe à l’exécution (ce que signalent gcc, la glibc, valgrind ou AddressSanitizer), et proposer une correction.
struct Personne {
char *nom;
int age;
};
/* Fragment 1 */
struct Personne *p = malloc(sizeof *p);
p->nom = "Fatou";
p->age = 25;
free(p->nom);
free(p);
/* Fragment 2 */
struct Personne a;
a.nom = malloc(6);
strcpy(a.nom, "Awa");
struct Personne b = a;
free(a.nom);
free(b.nom);/* Fragment 3 */
struct Personne *q = malloc(sizeof *q);
q->nom = malloc(7);
strcpy(q->nom, "Cheikh");
free(q);
free(q->nom);
/* Fragment 4 */
struct Chaine {
size_t len;
char data[];
};
struct Chaine *s =
malloc(sizeof(struct Chaine) + 8);
s->len = 8;
strcpy(s->data, "Linguere");- Pour le fragment 2, que valent
a.nometb.nomaprès la copie ? Écrire une fonctionstruct Personne copier(const struct Personne *src)qui fait une copie indépendante. - Pour le fragment 4, donner
sizeof(struct Chaine)et la taille à allouer.
Exercice 7 (approfondissement) : une liste chaînée dans le tas
Un programme insère trois nœuds struct Noeud { int valeur; struct Noeud *suivant; } en tête d’une liste, chacun alloué par malloc(sizeof(struct Noeud)). On relève le tas autour des nœuds ; dans la première colonne, les adresses sont abrégées à leurs trois derniers chiffres (0x2a0 désigne 0x5555555592a0), mais les octets sont recopiés tels quels. La variable tete vaut 0x5555555592e0.
| Addr | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 |
|---|---|---|---|---|---|---|---|---|
0x298 |
21 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x2a0 |
07 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x2a8 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x2b0 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x2b8 |
21 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x2c0 |
03 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x2c8 |
a0 |
92 |
55 |
55 |
55 |
55 |
00 |
00 |
0x2d0 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x2d8 |
21 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x2e0 |
09 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x2e8 |
c0 |
92 |
55 |
55 |
55 |
55 |
00 |
00 |
0x2f0 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
- Donner
sizeof(struct Noeud)et l’écart entre deux nœuds consécutifs dans le tas. Que représentent les octets21 00 00 00 00 00 00 00en0x298,0x2b8et0x2d8? - En partant de
tete, donner la valeur et le champsuivantde chaque nœud, jusqu’àNULL. - Donner les valeurs affichées par un parcours de la liste, puis l’ordre dans lequel les nœuds ont été alloués.
- Écrire la boucle qui libère toute la liste, et expliquer pourquoi
free(p); p = p->suivant;est incorrect.
Pour cette séance
Le cours
Les slides
À lire en ligne
À télécharger
- Code de départ du TP (ZIP, 5 Ko)