cours La mémoire : qui possède quoi 3/5

Le pointeur fantôme

Un pointeur distribué plus tôt pointe en silence vers de la mémoire libérée après la croissance du tableau.

Leçon

L'idée : realloc peut tout déplacer

Quand un tableau dynamique grandit, realloc peut le copier à une nouvelle adresse et libérer l'ancien bloc. Tout pointeur distribué vers l'ancien bloc est désormais pendant (dangling) : il pointe vers de la mémoire libérée.

Le lire est un use-after-free. Ça « marche » souvent encore, car la mémoire libérée n'est pas effacée tout de suite. C'est ce qui le rend dangereux.

La situation

store_open renvoie un Session * que les appelants conservent et utilisent plus tard, même après avoir ouvert d'autres sessions. Le tableau de sessions grandit avec realloc.

Votre mission

Lancez le sanitizer et lisez son rapport : où le bloc a été alloué, où il a été libéré, et quelle fonction l'a touché ensuite. Repensez ensuite la propriété pour que l'adresse d'une session ne change jamais, et expliquez précisément au junior comment faire.

À surveiller

  • Agrandir énormément la capacité initiale ne fait que repousser le déplacement. Et après ?
  • Modifier une struct, c'est aussi modifier le header, et la fonction qui libère tout.

Essayez cet exercice

Créez un compte gratuit pour dire au junior IA quoi changer, lancer les tests et le vérificateur mémoire, et obtenir un code de complétion vérifiable.

Code de départ
store.c
#include "store.h"#include <stdio.h>#include <stdlib.h> void store_init(Store *s) {    s->items = NULL;    s->len = 0;    s->cap = 0;    s->next_id = 1;} Session *store_open(Store *s, const char *user) {    if (s->len == s->cap) {        size_t cap = s->cap ? s->cap * 2 : 2;        Session *grown = realloc(s->items, cap * sizeof *grown);        if (!grown) return NULL;        s->items = grown;        s->cap = cap;    }    Session *x = &s->items[s->len++];    x->id = s->next_id++;    snprintf(x->user, sizeof x->user, "%s", user);    return x;} void store_close_all(Store *s) {    free(s->items);    s->items = NULL;    s->len = 0;    s->cap = 0;}
store.h
#ifndef STORE_H#define STORE_H#include <stddef.h> typedef struct {    int id;    char user[32];} Session; typedef struct {    Session *items; /* dynamic array of sessions */    size_t len;    size_t cap;    int next_id;} Store; void store_init(Store *s); /* Opens a session for `user` and returns a pointer to it. * Callers keep that pointer and use it later, even after opening more sessions. */Session *store_open(Store *s, const char *user); /* Frees everything the store owns. */void store_close_all(Store *s);#endif