Implementing free
Free: Marking Blocks for Reuse
Now that we have malloc, we need free(). Without it, memory can only grow - never be reused.
What free() Does
free() does NOT return memory to the OS. It marks the block as available for reuse:
Before free(p):
┌──────────────┬───────────────────┐
│ size=100 │ │
│ free=0 ────────► ALLOCATED │
│ next=... │ │
└──────────────┴───────────────────┘
After free(p):
┌──────────────┬───────────────────┐
│ size=100 │ │
│ free=1 ────────► FREE (reusable)│
│ next=... │ │
└──────────────┴───────────────────┘
The Critical Pointer Arithmetic
When the user calls free(ptr), they give us the pointer to their DATA. We need to find the HEADER:
void my_free(void *ptr) {
// ptr points to user data
// Header is immediately BEFORE the data
block_header_t *block = (block_header_t*)ptr - 1;
block->free = 1;
}
Your Task
// Get the block header from a user pointer
block_header_t *get_block(void *ptr);
// Free the memory pointed to by ptr
// - Find the block header
// - Mark it as free
// - Handle NULL gracefully (do nothing)
void my_free(void *ptr);
Safety Considerations
Real allocators add checks:
- NULL check:
free(NULL)should be a no-op (do nothing) - Double-free detection: Freeing already-freed memory is a bug
- Invalid pointer detection: Only free pointers from malloc
For now, just implement the basics with the NULL check.
Why Not Return Memory to OS?
Consider this sequence:
void *a = malloc(100); // Block A
void *b = malloc(100); // Block B
void *c = malloc(100); // Block C
free(b); // Free middle block
The heap looks like: [A used][B free][C used]
We can't shrink the heap because C is still in use! We can only shrink from the TOP. So free() just marks blocks for reuse.
Run tests to see results
No issues detected