Appearance
[中文版本](/posts/c教程/zh-CN/10-真实项目/095-Implementing a Custom Allocator)
Every C program eventually asks the operating system for memory, butmalloc andfree are not magic—they are layers above system calls likebrk andmmap. In this section, you will build your own custom memory allocator—a simple arena allocator that grabs a large block of memory once and doles it out efficiently.
You’ll see how real allocators work inside kernels, games, and embedded systems.
Step 1. The Goal
We’ll implement a minimal arena allocator that:
- Allocates from a preallocated block
- Never frees individual objects
- Resets all memory at once when the arena is cleared
This model is perfect for short-lived data structures, parsing, and high-performance applications.
Step 2. Design
An arena allocator tracks:
- The base pointer (start of memory)
- The current pointer (next free position)
- The capacity (total size of the arena)
When you allocate, it simply bumps the pointer forward.
Structure:
typedef struct {
unsigned char *base;
size_t capacity;
size_t offset;
} Arena;Step 3. Tiny Code: Minimal Arena
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
typedef struct {
unsigned char *base;
size_t capacity;
size_t offset;
} Arena;
Arena *arena_create(size_t capacity) {
Arena *a = malloc(sizeof(Arena));
if (!a) return NULL;
a->base = malloc(capacity);
if (!a->base) { free(a); return NULL; }
a->capacity = capacity;
a->offset = 0;
return a;
}
void *arena_alloc(Arena *a, size_t size) {
if (a->offset + size > a->capacity) return NULL;
void *ptr = a->base + a->offset;
a->offset += size;
return ptr;
}
void arena_reset(Arena *a) {
a->offset = 0;
}
void arena_free(Arena *a) {
free(a->base);
free(a);
}Step 4. Example Use
int main(void) {
Arena *arena = arena_create(1024);
if (!arena) {
fprintf(stderr, "Failed to create arena\n");
return 1;
}
int *arr = arena_alloc(arena, 10 * sizeof(int));
if (!arr) {
fprintf(stderr, "Allocation failed\n");
arena_free(arena);
return 1;
}
for (int i = 0; i < 10; i++) arr[i] = i * i;
printf("Squares: ");
for (int i = 0; i < 10; i++) printf("%d ", arr[i]);
printf("\n");
arena_reset(arena); // all memory reused
arena_free(arena);
return 0;
}Step 5. How It Works
arena_creategrabs one large block frommalloc.arena_allochands out memory by increasing an offset—no per-object metadata.arena_resetrewinds the arena to reuse the memory instantly.arena_freereleases the entire block in one call.
This is O(1) for every allocation, with zero fragmentation.
Step 6. Adding Alignment
Sometimes allocations must be aligned (for example, 16-byte alignment for SIMD). We can round up the offset to the nearest alignment boundary.
static size_t align_up(size_t n, size_t align) {
return (n + (align - 1)) & ~(align - 1);
}
void *arena_alloc_aligned(Arena *a, size_t size, size_t align) {
size_t new_offset = align_up(a->offset, align);
if (new_offset + size > a->capacity) return NULL;
void *ptr = a->base + new_offset;
a->offset = new_offset + size;
return ptr;
}Step 7. Debugging Helpers
Add diagnostic printing to understand usage:
void arena_stats(Arena *a) {
printf("Arena used: %zu / %zu bytes (%.1f%%)\n",
a->offset, a->capacity,
(a->offset * 100.0) / a->capacity);
}Step 8. Advanced Idea: Nested Arenas
You can make sub-arenas for scoped memory:
typedef struct {
Arena *parent;
size_t start;
} ArenaScope;
ArenaScope arena_push(Arena *a) {
return (ArenaScope){ .parent = a, .start = a->offset };
}
void arena_pop(ArenaScope s) {
s.parent->offset = s.start;
}This lets you “temporarily allocate” for a function or block and reset automatically.
Step 9. Why It Matters
Allocators define how performance feels in large systems. By writing one, you understand:
- How
mallocandfreemanage metadata - How fragmentation occurs
- How specialized allocators (arenas, pools, slabs) achieve speed and predictability
Games, web servers, and compilers all use custom allocators to control lifetime and avoid overhead.
Step 10. Try It Yourself
- Add bounds checking that prints errors when overrun.
- Implement a pool allocator for fixed-size objects (e.g.,
struct Node). - Use
mmapto request anonymous memory directly from the OS. - Add a leak detector that reports unfreed bytes at shutdown.
- Combine multiple arenas into a hierarchical allocator.
Next you’ll build 96. Writing a Text Parser, using your allocator to manage short-lived strings and tokens as you build a mini lexer and parser in pure C.
