make all # Build everything
make clean # Clean build artifacts
make run-test # Run tests
make run-bench # Run benchmarks
make run-example # Run example program
make help # Show all targets
#include "allocator.h"
// Allocate
void* ptr = mem_malloc(1024);
// Free
mem_free(ptr);
// Allocate and zero-initialize
int* arr = mem_calloc(100, sizeof(int));
// Resize
ptr = mem_realloc(ptr, 2048);
// Use _ts suffix for thread-safe functions
void* ptr = mem_malloc_ts(1024);
mem_free_ts(ptr);
// Print statistics
mem_print_stats();
// Get statistics programmatically
mem_stats_t stats = mem_get_stats();
printf("Current usage: %zu bytes\n", stats.current_usage);
| Function |
Description |
Thread-Safe |
mem_malloc(size) |
Allocate memory |
No |
mem_free(ptr) |
Free memory |
No |
mem_calloc(n, size) |
Allocate and zero |
No |
mem_realloc(ptr, size) |
Resize allocation |
No |
mem_malloc_ts(size) |
Allocate memory |
Yes |
mem_free_ts(ptr) |
Free memory |
Yes |
mem_calloc_ts(n, size) |
Allocate and zero |
Yes |
mem_realloc_ts(ptr, size) |
Resize allocation |
Yes |
mem_print_stats() |
Print statistics |
- |
mem_get_stats() |
Get statistics |
- |
MIN_BLOCK_SIZE 32 // Minimum block size
ALIGNMENT 16 // Memory alignment
NUM_SIZE_CLASSES 10 // Number of free lists
MMAP_THRESHOLD 131072 // 128KB - use mmap above this
BRK_INCREMENT 65536 // 64KB - heap growth size
| Class |
Max Size |
Typical Use |
| 0 |
32 B |
Tiny objects |
| 1 |
64 B |
Small structs |
| 2 |
128 B |
Cache-line sized |
| 3 |
256 B |
Medium structs |
| 4 |
512 B |
Small buffers |
| 5 |
1 KB |
Typical buffers |
| 6 |
2 KB |
Network packets |
| 7 |
4 KB |
Page-sized |
| 8 |
8 KB |
Large buffers |
| 9 |
>8 KB |
Very large |
void* ptr = mem_malloc(size);
if (!ptr) {
fprintf(stderr, "Allocation failed\n");
return -1;
}
void* new_ptr = mem_realloc(old_ptr, new_size);
if (!new_ptr && new_size != 0) {
mem_free(old_ptr);
return -1;
}
old_ptr = new_ptr;
int* arr = mem_calloc(count, sizeof(int));
// All elements initialized to 0
size_t len = strlen(str) + 1;
char* copy = mem_malloc(len);
strcpy(copy, str);
# Test for memory leaks
make valgrind
# Run benchmarks with Valgrind
make valgrind-bench
- Check return values: Always check if allocation succeeded
- Use statistics: Call
mem_print_stats() to track usage
- Set to NULL: After freeing, set pointer to NULL
- Valgrind: Run with Valgrind to catch errors
- Match calls: Ensure every malloc has a corresponding free
- Batch allocations: Allocate once, reuse many times
- Right-size: Don't over-allocate
- Thread-unsafe: Use non-_ts versions when possible
- Large allocs: >128KB allocations use mmap (fast to free)
- Zero-init: Use calloc instead of malloc+memset
| Error |
Cause |
Solution |
| Segmentation fault |
Use after free |
Don't use after calling free |
| Memory leak |
Missing free |
Free all allocations |
| Double free |
Freeing twice |
Set to NULL after free |
| Buffer overflow |
Writing beyond size |
Allocate enough space |
| NULL pointer |
Allocation failed |
Check return value |
allocator.h - Public API header
allocator.c - Core implementation
allocator_ts.c - Thread-safe wrappers
test.c - Test suite
benchmark.c - Performance benchmarks
example.c - Usage examples
Makefile - Build configuration
README.md - Main documentation
ARCHITECTURE.md - Design documentation
API.md - Detailed API reference
VALGRIND.md - Valgrind guide
- Main docs: README.md
- API reference: API.md
- Architecture: ARCHITECTURE.md
- Valgrind guide: VALGRIND.md