# How to Switch Between the Default and Stdlib Allocators at Runtime in Mulle-Allocator

> Learn how to switch between default and stdlib allocators at runtime in Mulle-Allocator. Discover methods for dynamic allocator selection in your C projects.

- Repository: [mulle-c/mulle-allocator](https://github.com/mulle-c/mulle-allocator)
- Tags: how-to-guide
- Published: 2026-03-07

---

**You can switch between `mulle_allocator_default` and `mulle_allocator_stdlib` at runtime either by passing explicit allocator pointers to individual allocation calls or by overwriting the global `mulle_allocator_default` struct with the contents of `mulle_allocator_stdlib`.**

The mulle-c/mulle-allocator library provides flexible memory management by exposing two distinct global allocator instances that can be toggled at runtime without recompiling. Whether you need to interoperate with code expecting standard libc behavior or debug allocation strategies, understanding how to switch between the default allocator and stdlib allocator at runtime gives you fine-grained control over memory operations.

## Understanding the Global Allocator Instances

Mulle-Allocator ships with two ready-to-use global structs defined in [[`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h)](https://github.com/mulle-c/mulle-allocator/blob/master/src/mulle-allocator.h):

```c
MULLE__ALLOCATOR_GLOBAL struct mulle_allocator   mulle_allocator_default;
MULLE__ALLOCATOR_GLOBAL struct mulle_allocator   mulle_allocator_stdlib;

```

**`mulle_allocator_default`** serves as the library's fallback allocator. When any public allocation function receives a `NULL` allocator pointer, it automatically routes the call through this global instance.

**`mulle_allocator_stdlib`** provides a thin wrapper around the C standard library functions (`calloc`, `realloc`, `free`). The implementation in [[`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c)](https://github.com/mulle-c/mulle-allocator/blob/master/src/mulle-allocator.c) initializes this struct with function pointers to static wrappers:

```c
static void *v_calloc(size_t n, size_t size, struct mulle_allocator *allocator) { … }
static void *v_realloc(void *block, size_t size, struct mulle_allocator *allocator) { … }
static void  v_free(void *block, struct mulle_allocator *allocator) { … }

struct mulle_allocator   mulle_allocator_stdlib = {
    v_calloc,
    v_realloc,
    v_free,
    mulle_allocation_fail,
    mulle_allocator_no_aba_abort,
    NULL
};

```

## Method 1: Per-Call Selection

The safest approach to switch allocators at runtime is to pass explicit allocator pointers to individual API calls. This method avoids global state changes and ensures specific allocations use your chosen strategy without affecting other parts of the program.

```c
// Use stdlib allocator for this specific allocation
void *p = mulle_allocator_malloc(&mulle_allocator_stdlib, 128);

// Use default allocator for another allocation
void *q = mulle_allocator_malloc(&mulle_allocator_default, 64);

// Standard calloc using default behavior
void *r = mulle_calloc(10, sizeof(int));  // Uses mulle_allocator_default internally

```

All public allocation functions—including `mulle_allocator_malloc()`, `mulle_allocator_calloc()`, and `mulle_allocator_realloc()`—accept an explicit `struct mulle_allocator *` as their first parameter. When you pass `&mulle_allocator_stdlib`, the call routes directly to the C standard library functions. When you pass `&mulle_allocator_default` or `NULL`, the call uses the library's default allocation strategy.

## Method 2: Global Runtime Switch

To change the allocator for all subsequent calls that rely on the default (including those passing `NULL`), overwrite the global `mulle_allocator_default` struct at runtime. Because these are plain mutable structs rather than `const` pointers, standard C assignment works without special API calls.

```c
/* Preserve the original default allocator for later restoration */
struct mulle_allocator saved_default = mulle_allocator_default;

/* Switch global default to use stdlib implementations */
mulle_allocator_default = mulle_allocator_stdlib;

/* All subsequent calls using NULL or implicit default now use stdlib */
void *p = mulle_malloc(256);   // Internally uses libc calloc/realloc/free
void *q = mulle_calloc(10, sizeof(char));  // Also uses stdlib

/* Restore the original default allocator */
mulle_allocator_default = saved_default;

```

This technique affects every allocation throughout the process that does not explicitly specify an allocator pointer. Use this approach when integrating with third-party libraries that expect standard libc allocation semantics or when profiling memory behavior under different allocation strategies.

## Verifying the Active Allocator

When debugging or writing conditional logic based on the current allocator, use the `mulle_allocator_is_stdlib_allocator()` helper function defined in [[`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c)](https://github.com/mulle-c/mulle-allocator/blob/master/src/mulle-allocator.c):

```c
if (mulle_allocator_is_stdlib_allocator(&mulle_allocator_default)) {
    printf("Currently using stdlib allocator\n");
} else {
    printf("Using custom or default allocator\n");
}

```

This function compares the function pointers within the struct to determine if the allocator forwards directly to standard library functions.

## Summary

- **Mulle-Allocator** provides two global instances: `mulle_allocator_default` (optimized default behavior) and `mulle_allocator_stdlib` (libc forwarding).
- **Per-call switching** passes `&mulle_allocator_default` or `&mulle_allocator_stdlib` explicitly to allocation functions, avoiding global side effects.
- **Global switching** assigns `mulle_allocator_default = mulle_allocator_stdlib` to redirect all default allocations process-wide.
- **Detection** uses `mulle_allocator_is_stdlib_allocator()` to verify which implementation is active.
- Both instances are defined in [`src/mulle-allocator.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.h) and implemented in [`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c).

## Frequently Asked Questions

### What happens if I pass NULL to mulle_malloc or related functions?

When you pass `NULL` as the allocator pointer to any Mulle-Allocator function, the call automatically falls back to `mulle_allocator_default`. This global struct contains the function pointers that determine actual memory operations, so overwriting `mulle_allocator_default` at runtime changes the behavior of all NULL-passing calls.

### Is it safe to modify mulle_allocator_default at runtime in multithreaded programs?

Because `mulle_allocator_default` is a plain global struct (not thread-local), overwriting it affects all threads immediately. You should only perform global switches during initialization phases or when you can guarantee no other thread is performing allocations. For thread-safe per-call selection, always pass explicit allocator pointers rather than modifying global state.

### How do I revert to the original default after switching to the stdlib allocator?

Store a copy of the original struct before switching: `struct mulle_allocator saved = mulle_allocator_default;`. After performing operations with the stdlib allocator, restore the original behavior with `mulle_allocator_default = saved;`. This preserves any internal state or configuration specific to the library's default allocator.

### Can I detect if an arbitrary allocator pointer refers to the stdlib implementation?

Yes. Pass the allocator pointer to `mulle_allocator_is_stdlib_allocator()` as implemented in [`src/mulle-allocator.c`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-allocator.c). This function checks whether the allocator's function pointers match the standard library wrappers, returning a boolean indicating if the allocator is the stdlib instance or a custom implementation.