The Heap and Data Lifetime
Where data lives is only half the question
Section titled “Where data lives is only half the question”Researchers often ask whether data is “on the stack” or “on the heap.” The next question should be: how long is that storage valid? A pointer can retain an address after the object at that address has ceased to exist. The number still looks convincing while the right to use it has vanished.
Three useful C storage categories are:
| Storage | Typical lifetime |
|---|---|
| Automatic local storage | Until execution leaves its enclosing block or function |
| Dynamically allocated storage | From successful allocation until it is released |
| Static storage | For the lifetime of the process |
Scope concerns where a name can be used in source code. Lifetime concerns when the referenced object exists. They often align, but they are not the same concept.
Allocate, use, and release
Section titled “Allocate, use, and release”Create lifetime.c:
#include <stdio.h>#include <stdlib.h>
int main(void) { int local = 7; int *dynamic = malloc(sizeof(*dynamic)); if (dynamic == NULL) { return 1; }
*dynamic = 42; printf("local=%d dynamic=%d\n", local, *dynamic); free(dynamic); dynamic = NULL; return 0;}Compile and run it:
gcc -Wall -Wextra -O0 -g -o lifetime lifetime.c./lifetimemalloc requests storage and returns its address, or NULL if the request fails. Dereferencing dynamic with *dynamic accesses the allocated integer. free ends the allocation’s lifetime. Assigning NULL afterward does not repair other copies of the pointer, but it prevents this variable from continuing to advertise the old address as usable.
Knowledge check
Question 1 of 2
Knowledge check complete
You answered all 2 questions correctly.
Three common lifetime failures
Section titled “Three common lifetime failures”- A memory leak loses the ability or intention to free an allocation that is no longer needed.
- A use-after-free accesses storage after it has been released.
- A double-free attempts to release the same live allocation twice without a new allocation in between.
These are not interchangeable “heap bugs.” They represent different broken claims about ownership and lifetime. Later tools will help us observe them; for now, make the claim explicit: who owns this allocation, and at which instruction does that ownership end?
Observe the heap mapping
Section titled “Observe the heap mapping”Pause the program before free by temporarily adding getchar(); after printf, then recompile and run it. In another terminal, find its PID and inspect:
pgrep -n lifetimegrep '\[heap\]' "/proc/$(pgrep -n lifetime)/maps"Press Enter in the first terminal to let it finish. Small allocations may be served from an existing heap mapping; larger allocations can use separate anonymous mappings. An allocator is a runtime manager layered on top of memory supplied by Linux, not the heap itself.
What this gives us as researchers
Section titled “What this gives us as researchers”You can now discuss memory bugs in terms of object lifetime and ownership rather than treating every pointer as valid because it contains a nonzero number. The final concept lesson follows programs across the boundary into the kernel.