Ownership, Lifetime, Leaks, and Dangling Pointers
This chapter defines ownership as a discipline for managing allocated memory, and explains the two failure modes that ownership prevents: leaks and dangling pointers.
Why This Matters
C has no garbage collector. Whether a block is freed, and when, is entirely your responsibility. Ownership is the rule that makes this tractable; leaks and dangling pointers are what happen when it is absent.
Prerequisites
c.memory.2— malloc/calloc/realloc/free.
Core Concept
Ownership
Every allocated block has exactly one owner — the code responsible for freeing it. Ownership can be:
- Transferred: the owner passes the pointer to another owner, which then
frees it.
- Shared: multiple parties use the pointer, but one still owns it (or a
reference-counting scheme manages it).
The rule: the owner frees, exactly once.
Leaks
A memory leak is a block that is no longer reachable but was never freed. Leaks waste memory; in long-running programs they accumulate and eventually exhaust memory.
void f(void)
{
int *p = malloc(sizeof *p); /* allocated */
/* ... no free, no transfer ... */
} /* p goes out of scope; block leaks */
Dangling pointers
A dangling pointer is a pointer to an object whose lifetime has ended. The classic case is free(p) followed by *p (use-after-free).
int *p = malloc(sizeof *p);
free(p);
*p = 5; /* use-after-free: UB */
Examples
Ownership transfer
int *make_int(int value)
{
int *p = malloc(sizeof *p);
if (p) *p = value;
return p; /* ownership transferred to caller */
}
int main(void)
{
int *p = make_int(42);
/* caller now owns p */
free(p);
return 0;
}
Setting a freed pointer to NULL
int *p = malloc(sizeof *p);
free(p);
p = NULL; /* helps catch use-after-free, but not a cure-all */
Setting to NULL prevents accidental reuse through p, but it does not fix other copies of the pointer that may still dangle.
How It Works
The heap has no notion of "reachable." A leak is purely a property of your program's logic: if no pointer variable can reach a block, it is leaked. A dangling pointer exists when the object is gone but a pointer value still refers to its former address.
Variations
Reference counting
Shared ownership can be implemented by a count that is incremented on each new reference and decremented on release; when it reaches zero, the block is freed. This is the basis of retain/release patterns.
Arenas
An arena owns many allocations and frees them all at once (c.mem.arena), simplifying ownership for short-lived groups of objects.
Common Mistakes
- Forgetting to free on every error path (use
goto cleanup). - Freeing a block still owned by another part of the program.
- Storing a pointer to a local beyond the local's lifetime.
- Returning a pointer to a local variable.
Undefined Behavior
- Use-after-free (dangling pointer dereference).
VERIFIED - Double free.
VERIFIED - Accessing an object outside its lifetime.
VERIFIED
Portability
- Leaks and dangling pointers are semantic bugs, not implementation-specific
behavior. The consequences (memory exhaustion, corruption) are universal.
Under the Hood
A dangling pointer still holds the old address; the allocator may have reused that memory for a different object. Reading through it yields whatever is now there — often corrupting unrelated data. This is why use-after-free is so dangerous.
Practical Usage
- Document ownership in every API ("takes ownership," "borrows," "returns a
new object the caller must free").
- Free on every error path with
goto cleanup. - Prefer clear single-owner designs; use arenas or refcounting for shared
ownership.
Exercises
1. Write a function that returns an allocated object and document ownership. 2. Write a function with two error paths and show a leak, then fix it with goto cleanup. 3. Demonstrate use-after-free and observe it under ASan. 4. Explain why p = NULL after free helps but is insufficient.
Deep Challenge
Design a small reference-counted string type with create, retain, release, and accessor functions. Ensure release frees exactly once when the count reaches zero, and explain the thread-safety implications (and how atomics would help).
Related Concepts
c.memory.2— allocation functions.c.mem.arena— arenas.c.debug.asan— ASan detection.c.core.7— lifetime.
References
- ISO/IEC 9899:2018 §6.2.4 (lifetime), §7.22.3 (allocation).
Verification
- Ownership is a convention, not a language feature.
VERIFIED - Use-after-free and double free are UB.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Ownership model
- [ ] Leaks
- [ ] Dangling pointers and use-after-free
- [ ] Ownership transfer
- [ ] Reference counting
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.mem.ownership | 0 | 7 |
| c.mem.leaks | 0 | 6 |
| c.mem.dangling | 0 | 7 |