courses Memory: who owns what 3/5

The Ghost Pointer

A pointer handed out earlier silently points into freed memory after the array grows.

Lesson

The idea: realloc can move everything

When a dynamic array grows, realloc may copy it to a new address and free the old block. Every pointer you handed out into the old block is now dangling: it points to freed memory.

Reading through it is a use-after-free. It often still "works" because freed memory is not erased right away. That is what makes it dangerous.

The situation

store_open returns a Session * that callers keep and use later, even after opening more sessions. The array of sessions grows with realloc.

Your mission

Run the sanitizer and read its report: where the block was allocated, where it was freed, and which function touched it afterwards. Then redesign the ownership so a session's address never changes, and tell the junior exactly how.

What to watch for

  • Making the initial capacity huge only postpones the move. What happens after that?
  • Changing a struct means changing the header too, and the function that frees everything.

Try this exercise

Create a free account to tell the AI junior what to change, run the tests and the memory checker, and earn a completion code you can verify.

Starting code
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