Dynamic Memory Allocation
Dynamic Memory Allocation
So far, all variables have been allocated on the stack with a fixed size known at compile time. Dynamic memory allocation lets you request memory at runtime from the heap.
Heap vs Stack
Stack and heap are two regions of memory with very different characteristics:
| Stack | Heap | |
|---|---|---|
| Allocation | Automatic (compiler manages) | Manual (malloc/free) |
| Speed | Very fast (just moves stack pointer) | Slower (must find free block) |
| Lifetime | Until function returns | Until you call free |
| Size | Small (typically 1-8 MB) | Large (limited by system RAM) |
| Access | Local variables only | Accessible via pointers from anywhere |
| Fragmentation | None | Can fragment over time |
Use the stack for small, short-lived variables. Use the heap when you need memory to outlive a function call, when the size is determined at runtime, or when you need large allocations.
void example() {
int x = 10; // stack: fast, automatic
int *p = (int *)malloc(sizeof(int)); // heap: manual, flexible
*p = 20;
free(p); // you must free heap memory
} // x is automatically freed when function returns
malloc
malloc(size) allocates size bytes and returns a pointer to the memory:
#include <stdlib.h>
int *p = (int *)malloc(sizeof(int));
*p = 42;
printf("%d\n", *p); // 42
free(p); // always free when done
The replicator:
mallocmaterializes memory from thin air, andfreerecycles it back into energy.
sizeof
Use sizeof to get the size of a type in bytes:
sizeof(int) // 4 bytes
sizeof(char) // 1 byte
sizeof(long) // 8 bytes
Dynamic Arrays
malloc is often used to create arrays whose size is determined at runtime:
int n = 5;
int *arr = (int *)malloc(n * sizeof(int));
for (int i = 0; i < n; i++) {
arr[i] = i * 10;
}
// arr[0]=0, arr[1]=10, arr[2]=20, ...
free(arr);
free
Every malloc must be paired with a free to avoid memory leaks:
int *p = (int *)malloc(sizeof(int));
*p = 99;
free(p); // release the memory
// p is now a dangling pointer -- don't use it!
Memory Debugging with Valgrind
Memory bugs are among the hardest to track down. Common memory errors include:
- Memory leak -- forgetting to
freeallocated memory - Use after free -- reading/writing memory after calling
free - Double free -- calling
freeon the same pointer twice - Buffer overflow -- writing past the end of an allocated block
In a real development environment, Valgrind is the standard tool for catching these bugs:
gcc -g my_program.c -o my_program
valgrind --leak-check=full ./my_program
Valgrind will report exactly where leaks and invalid accesses occur. Always run it before shipping C code. A clean Valgrind report (0 errors, 0 leaks) is the gold standard for C memory safety.
Your Task
Write a function int *make_range(int n) that allocates an array of n integers and fills it with values 1 through n. In main, call it with n=5, print each value on its own line, then free the memory.