Every C program leans on malloc and free without much thought.
In this post we rip out the guts of a simple allocator and build one from scratch.
The OS hands your process raw pages via sbrk() or mmap().
Your allocator’s job is to carve that slab into chunks and hand them out on request.
typedef struct block {
size_t size;
int free;
struct block *next;
} block_t;
#define BLOCK_SIZE sizeof(block_t)
We keep a linked list of every allocated block. On malloc:
sbrk(size)void *my_malloc(size_t size) {
block_t *cur = head;
while (cur) {
if (cur->free && cur->size >= size) {
cur->free = 0;
return (void *)(cur + 1); /* skip header */
}
cur = cur->next;
}
/* grow heap */
block_t *blk = sbrk(BLOCK_SIZE + size);
blk->size = size;
blk->free = 0;
blk->next = NULL;
/* ... append to list */
return (void *)(blk + 1);
}
sbrk doesn’t guarantee alignment. Unaligned access on x86 is slow;
on ARM it’s a bus fault. Always round up to sizeof(max_align_t):
#define ALIGN(n) (((n) + sizeof(max_align_t) - 1) & ~(sizeof(max_align_t) - 1))
Naive free causes fragmentation — lots of small gaps that can’t
satisfy a large request even if total free memory is sufficient.
Fix it by merging adjacent free blocks during free().
mmap instead of sbrk for large allocations