The Heap: Memory at Runtime
So far, we have used Automatic Storage (local variables on the stack). However, the stack has limitations:
- Fixed Size: You must know the size at compile-time (or use VLAs, which are controversial).
- Limited Lifetime: Variables die when the function returns.
The Heap is a large pool of memory that exists independently of function calls. We can request memory from the heap at any time and it stays allocated until we explicitly release it.
The Allocation Quadruplet: <stdlib.h>
C provides four primary functions for managing heap memory:
| Function | Purpose | Key Detail |
|---|---|---|
malloc(size) | Allocates size bytes. | Memory is uninitialized (contains garbage). |
calloc(n, size) | Allocates n elements of size. | Memory is zero-initialized. |
realloc(ptr, size) | Resizes an existing block. | May move the block to a new address. |
free(ptr) | Releases the block. | Using the pointer after free is Undefined Behavior. |
The Lifecycle of an Allocation
- Request:
int *p = malloc(10 * sizeof(int)); - Safety Check: Always check if
mallocreturnedNULL(which happens if the system is out of memory). - Usage: Use the pointer just like an array.
- Cleanup:
free(p);
Interactive Lab
Waiting for signal...
The Dangers of Manual Management
In languages like Python or Java, a Garbage Collector cleans up after you. In C, you are the garbage collector.
1. Memory Leaks
A leak occurs when you lose the pointer to an allocated block without calling free(). The memory remains “reserved” but unusable, eventually crashing the system if it happens in a loop.
2. Use-After-Free
Dereferencing a pointer after it has been passed to free(). The memory might have been re-assigned to something else, causing silent data corruption.
3. Double Free
Calling free() on the same pointer twice. This usually crashes the program immediately as it corrupts the heap’s internal metadata.
The realloc Pattern
int *p = malloc(10 * sizeof(int)); // We need more space! int *temp = (p, 20 * sizeof(int)); if (temp != NULL) p = temp;
Memory Fragmentation
Over time, frequent allocations and deallocations can leave “holes” in the heap—small blocks of free memory that are too small to satisfy new requests. High-performance systems often use Custom Allocators (like jemalloc or mimalloc) to mitigate this.