TD — Séance 3 — Organisation de la mémoire
Dumps, pointeurs, adresses, chaînes et tas
Lire et écrire l’état de la mémoire octet par octet : retrouver des valeurs dans un dump, calculer des adresses à partir de l’arithmétique des pointeurs, suivre des pointeurs et une double indirection, et reconnaître les erreurs de gestion des chaînes et du tas. Sauf mention contraire, la machine est un x86-64 little-endian : char 1 octet, short 2, int 4, double et pointeurs 8. Les adresses sont écrites en hexadécimal.
Exercice 1 : endianness
La valeur 0x0A1B2C3D est rangée sur 4 octets à partir de l’adresse 0x300. À l’adresse 0x400, un dump a été relevé sur une machine little-endian.
| Addr | +0 | +1 | +2 | +3 |
|---|---|---|---|---|
0x300 (big) |
||||
0x300 (little) |
||||
0x400 |
9c |
ff |
ff |
ff |
- Remplir la ligne
0x300en big-endian, puis en little-endian. - Sur la machine little-endian, donner l’octet lu à l’adresse
0x302. - Sur la machine little-endian, donner en hexadécimal le
shortlu en0x300, puis celui lu en0x302. - Donner en hexadécimal et en décimal l’
intrangé en0x400. - Donner en hexadécimal et en décimal le
shortrangé en0x400. Expliquer pourquoi il a la même valeur décimale que l’int. - Donner l’
intque lirait une machine big-endian à partir des mêmes 4 octets.
Exercice 2 : lire un dump
On relève le dump suivant, de 0x40 à 0x6f :
| Addr | +0 | +1 | +2 | +3 | +4 | +5 | +6 | +7 |
|---|---|---|---|---|---|---|---|---|
0x40 |
05 |
00 |
00 |
00 |
a0 |
0f |
00 |
00 |
0x48 |
4d |
62 |
6f |
75 |
72 |
00 |
7f |
33 |
0x50 |
00 |
00 |
00 |
00 |
00 |
40 |
8f |
40 |
0x58 |
48 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
0x60 |
18 |
fc |
2c |
01 |
53 |
4e |
00 |
21 |
0x68 |
60 |
00 |
00 |
00 |
00 |
00 |
00 |
00 |
int *pi = (int *)0x40;
char *ch = (char *)0x48;
double *pd = (double *)0x50;
char **pc = (char **)0x58;
short *ps = (short *)0x60;
void **pv = (void **)0x68;Codes ASCII utiles : 21 !, 4d M, 4e N, 53 S, 62 b, 6f o, 72 r, 75 u.
Pour chaque expression, donner le type C et la valeur (en hexadécimal, puis en décimal pour les nombres) :
pi[0]etpi[1];- la chaîne
ch, puisstrlen(ch)et*(ch + 2); *pd: les 8 octets lus, puis le signe, l’exposant et la mantisse au format IEEE 754, et la valeur décimale ;*pc,(*pc)[1], et la chaîne désignée par*pc;ps[0]etps[1];*(short *)(ch + 1);*pv, puis*(short *)*pv;strlen((char *)0x64).
Exercice 3 : adresses et arithmétique des pointeurs
short v[8]; /* v en 0x5000 */
short *p = v + 3;
short *q = &v[7];
int g[4][5]; /* g en 0x6000 */
double h[3][2]; /* h en 0x6100 */- Donner
petq, puisq - pet(char *)q - (char *)p. - Donner
p + 2etp - 3. Que désignep - 3? - Donner
(double *)0x5000 + 3,(int *)0x5000 + 5et(char *)0x5000 + 5. - Donner
&g[2][3]et&g[3][0], en expliquant le calcul. - Donner le type et la valeur de
g[2], puis&g[3][4] - &g[0][0]. - Donner
sizeof getsizeof g[1]. - Donner
&h[2][1].
Exercice 4 : l’état de la mémoire ligne par ligne
Le tableau t est rangé en 0x80, le pointeur p en 0x90 et le pointeur q en 0x98.
int t[3] = {7, 8, 9};
int *p = t + 1;
int **q = &p;
*p = *p * 10;
(*q)++;
**q += t[0];
p[-2] = -1;Compléter le tableau : la valeur de chaque objet après l’exécution de chaque ligne (une case vide si l’objet n’existe pas encore).
Ligne t[0]t[1]t[2]pq*p**q1 2 3 4 5 6 7 Remplir le dump de la zone
0x80à0x9faprès la ligne 7. Que contiennent les octets0x8cà0x8f?Addr +0 +1 +2 +3 +4 +5 +6 +7 0x800x880x900x98À la ligne 5,
(*q)++modifie-t-ilq,pout[1]? Justifier.Écrire, sans utiliser
p, une expression qui désignet[2]à partir deq.
Exercice 5 : chaînes de caractères
Le tableau ville est rangé en 0x70 ; son contenu initial est inconnu (noté ??).
char ville[12];
strcpy(ville, "Podor");
strcat(ville, "-Matam");Codes ASCII utiles : 2d -, 4d M, 50 P, 61 a, 64 d, 6d m, 6f o, 72 r, 74 t.
Remplir le dump de
villeaprès la ligne 2, puis après la ligne 3.Addr +0 +1 +2 +3 +4 +5 +6 +7 0x700x78Donner
strlen(ville)etsizeof villeaprès la ligne 3.Donner la chaîne
ville + 6et sa longueur.Combien d’octets libres reste-t-il dans
villeaprès la ligne 3 ? Une ligne 4 exécutestrcat(ville, "!"): que se passe-t-il ?Avec
char *nom = "Podor";, donnersizeof nomet dire sinom[0] = 'p';est correct.
Exercice 6 : erreurs de gestion du tas
Pour chaque fragment, donner le nombre d’octets alloués, l’erreur commise, sa conséquence (ce que signale gcc, AddressSanitizer ou valgrind, ou ce qui arrive à l’exécution), et une correction.
/* Fragment 1 */
int *v = malloc(10 * sizeof(int));
v = realloc(v, 20 * sizeof(int));
/* Fragment 2 */
for (int k = 0; k < 3; k++) {
char *s = malloc(32);
snprintf(s, 32, "releve %d", k);
printf("%s\n", s);
}
/* Fragment 3 */
int loc[4] = {1, 2, 3, 4};
int *p = loc;
free(p);
/* Fragment 4 */
char *c = malloc(6);
strcpy(c, "Kaffrine");
/* Fragment 5 */
int *w = calloc(4, sizeof(int));
free(w);
w[0] = 1;Exercice 7 (approfondissement) : inverser l’ordre des octets
Les protocoles réseau transmettent les entiers en big-endian. Une machine little-endian qui envoie l’adresse IPv4 192.168.10.1, soit l’entier 0xC0A80A01, doit donc inverser l’ordre de ses octets.
- Donner le dump des 4 octets de
x = 0xC0A80A01sur la machine little-endian. - Écrire la fonction
uint32_t inverse_octets(uint32_t x)qui rend l’entier aux octets inversés, à l’aide des seuls opérateurs>>,<<,&et|, une instruction par octet. - Donner, pour
x = 0xC0A80A01, la valeur de chacun des quatre termes calculés par la fonction, puis le résultat. - Donner le dump du résultat en mémoire. Que vaut
inverse_octets(inverse_octets(x))?
Pour cette séance
Le cours
Les slides
À lire en ligne
À télécharger
- Code de départ du TP (ZIP, 4 Ko)