Dynamic Memory Allocation
Objectives
- Understand the difference between stack and heap
- Master
malloc,calloc,realloc, andfree - Avoid common errors (memory leaks, double free)
Memory Layout
A C program uses several memory regions:
| Region | Description | Characteristics |
|---|---|---|
| Code | Program instructions | Read-only |
| Data | Global and static variables | Fixed size at compile time |
| Stack | Local variables | LIFO, limited size (~8MB) |
| Heap | Dynamic allocation | Large size, manually managed |
Stack vs Heap
- Stack: automatic allocation, fast, but limited in size
- Heap: manual allocation with malloc, slower but practically unlimited
Allocation Functions
malloc - Memory ALLOCation
#include <stdlib.h>
// Allocate n bytes, contents uninitialized
void* malloc(size_t n);
Example:
// Allocate an array of 10 integers
int* tab = (int*)malloc(10 * sizeof(int));
if (tab == NULL) {
// Allocation failed
exit(EXIT_FAILURE);
}
calloc - Clear ALLOCation
// Allocate n elements of size size, initialized to 0
void* calloc(size_t n, size_t size);
Example:
// Allocate and initialize to 0
int* tab = (int*)calloc(10, sizeof(int));
realloc - REALLOCation
// Resize a memory block
void* realloc(void* ptr, size_t new_size);
Example:
// Grow the array from 10 to 20 elements
tab = (int*)realloc(tab, 20 * sizeof(int));
Be careful with realloc
realloc may move the memory block. Always use the returned pointer:
int* new_tab = realloc(tab, new_size);
if (new_tab == NULL) {
// Keep the old tab intact
free(tab);
exit(EXIT_FAILURE);
}
tab = new_tab; free - Deallocation
// Free allocated memory
void free(void* ptr);
Common Errors
1. Memory leak
void memory_leak() {
int* p = malloc(100 * sizeof(int));
// ... use of p
// Forgot to call free(p)!
} // Memory remains allocated but inaccessible
2. Double free
void double_free() {
int* p = malloc(sizeof(int));
free(p);
free(p); // ERROR: undefined behavior!
}
3. Use after free
void use_after_free() {
int* p = malloc(sizeof(int));
*p = 42;
free(p);
printf("%d", *p); // ERROR: accessing freed memory!
}
4. Buffer overflow
void buffer_overflow() {
int* tab = malloc(10 * sizeof(int));
for (int i = 0; i <= 10; i++) { // <= instead of <
tab[i] = i; // ERROR: writing out of bounds!
}
free(tab);
}
Best Practices
- Always check the return value of
malloc/calloc/realloc - Always free allocated memory
- Set to NULL after
freeto avoid use-after-free - Use valgrind to detect leaks
// Safe allocation pattern
int* safe_malloc(size_t n) {
int* ptr = (int*)malloc(n * sizeof(int));
if (ptr == NULL) {
fprintf(stderr, "Allocation error\n");
exit(EXIT_FAILURE);
}
return ptr;
}
// Safe deallocation pattern
void safe_free(int** ptr) {
if (ptr != NULL && *ptr != NULL) {
free(*ptr);
*ptr = NULL; // Prevents double free
}
}
Using valgrind
# Compile with -g to get line numbers
gcc -g programme.c -o programme
# Check for memory leaks
valgrind --leak-check=full ./programme
Typical output:
==12345== HEAP SUMMARY:
==12345== in use at exit: 0 bytes in 0 blocks
==12345== total heap usage: 5 allocs, 5 frees, 1,024 bytes allocated
==12345== All heap blocks were freed -- no leaks are possible
Summary
| Function | Usage | Initialization |
|---|---|---|
malloc(size) | Simple allocation | No |
calloc(n, size) | Array allocation | Yes (to 0) |
realloc(ptr, size) | Resizing | Preserves existing data |
free(ptr) | Deallocation | - |