L'adresse d'une locale, et ce qu'elle vaut trois lignes plus loin
Exercice · niveau 2 · informatique (MP2I/MPI), chapitre 8 — Mémoire, fichiers et entrées-sorties
Énoncé
Ce programme compile. Qu'affiche-t-il ?
int* faute(void) {
int x = 42;
return &x;
}
void bruit(void) {
int a = 111, b = 222, c = 333;
printf("(bruit : %d %d %d)\n", a, b, c);
}
int main(void) {
int* p = faute();
printf("juste apres : %d\n", *p);
bruit();
printf("apres bruit : %d\n", *p);
return 0;
}Corrigé
Sortie mesurée :
juste apres : 42
(bruit : 111 222 333)
apres bruit : 1
Et c'est là tout le danger : la première ligne est juste. Le bloc d'activation de faute a été dépilé, mais la case n'a pas été effacée — rien ne l'efface — et personne n'est encore venu s'y installer. Le programme paraît donc fonctionner.
L'appel à bruit réutilise cette même région de pile pour ses propres locales. Après lui, *p vaut , une valeur qui ne vient de nulle part. Un bogue de cette famille survit à toutes les exécutions de mise au point et se réveille le jour où l'on insère un appel de plus.
Le compilateur l'avait dit. gcc -Wall produit :
warning: address of stack memory associated with local variable 'x' returned
C'est un avertissement qu'on ne discute pas : il n'a pas de faux positif.
Les trois corrections possibles, par ordre de préférence :
- rendre la valeur et non l'adresse :
int faute(void) { return 42; }; - écrire dans une case fournie par l'appelant :
void f(int* sortie); - allouer sur le tas — et documenter que l'appelant devra libérer.
La troisième est la seule qui rende un pointeur, et c'est la seule qui déplace une charge sur l'appelant. On ne la choisit que si l'objet doit vraiment survivre.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.