# Difference between mulle_alloca_do and mulle_calloca_do in mulle-allocator

> Discover the key difference between mulle_alloca_do and mulle_calloca_do in mulle-allocator. Learn how they handle memory initialization for efficient stack and heap allocation.

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

---

**The only difference between `mulle_alloca_do` and `mulle_calloca_do` is that the former returns uninitialized memory (like `malloc`) while the latter returns zero-initialized memory (like `calloc`), with both using an identical stack-first allocation strategy that automatically falls back to the heap when the requested size exceeds 128 bytes.**

The **mulle-allocator** library provides these macros in [`src/mulle-alloca.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-alloca.h) as a portable, safe alternative to variable-length arrays and `alloca()`. Both allocate temporary buffers that automatically clean up when execution leaves the enclosing block, but they differ critically in their initialization guarantees and choice of fallback allocator.

## Memory Initialization: The Primary Distinction

The fundamental distinction lies in what happens when the memory is first accessed. Both macros attempt to use a fixed-size stack buffer first (controlled by `MULLE_ALLOCA_STACKSIZE`, defaulting to 128 bytes), but behave differently when allocating from the heap or initializing the stack buffer.

### mulle_alloca_do (Uninitialized)

As implemented at lines 280-296 of [`src/mulle-alloca.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-alloca.h), this macro:

- Declares a stack buffer without initialization
- Falls back to **`mulle_malloc`** for heap allocations
- Leaves memory contents **indeterminate**—you must write before reading

Use this when you plan to immediately overwrite every element, such as when reading data from a file or socket into the buffer.

### mulle_calloca_do (Zero-Initialized)

As implemented at lines 505-525 of [`src/mulle-alloca.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-alloca.h), this macro:

- Declares the stack buffer with `{ 0 }` initialization
- Falls back to **`mulle_calloc`** for heap allocations  
- Guarantees **zero-filled** memory—safe to read without writing

Use this when constructing data structures with optional fields or when accumulating values where starting from zero is required.

## Internal Implementation Details

Both macros expand to a nested `for` loop that manages the buffer lifecycle automatically. The mechanism works as follows:

1. **Stack reservation**: A stack array of `MULLE_ALLOCA_STACKSIZE` bytes is declared
2. **Size check**: The requested count is stored in a hidden pointer variable (e.g., `name__count`) and tested against the stack capacity using `_mulle_alloca_do_get_size_as_length`
3. **Allocation decision**: 
   - If the data fits on the stack, the pointer `name` references the stack array
   - If the data exceeds the stack size, `mulle_malloc` (for `mulle_alloca_do`) or `mulle_calloc` (for `mulle_calloca_do`) is called
4. **Automatic cleanup**: When the `for` loop exits, `_mulle_alloca_do_free` releases the heap block if one was allocated, while stack memory is automatically reclaimed

The zero-initialization in `mulle_calloca_do` is achieved by the `{ 0 }` initializer on the stack array and the zeroing behavior of `mulle_calloc` for heap fallbacks.

## Practical Code Examples

### Allocating Uninitialized Temporary Storage

When you will immediately write to every byte, use `mulle_alloca_do` to avoid the overhead of zeroing:

```c
size_t n = 200;                     // Larger than default 128-byte stack buffer
mulle_alloca_do(buf, int, n)        // buf is an `int *`
{
    for (size_t i = 0; i < n; ++i)
        buf[i] = (int)i;            // Must write every element before use
    process_data(buf, n);
    /* buf is valid only inside this block */
}
/* Heap memory automatically freed here if n was large */

```

*If `n` fits within 128 bytes, `buf` lives on the stack; otherwise `mulle-allocator` allocates via `mulle_malloc`.*

### Allocating Zero-Filled Temporary Storage

When you need to read from the buffer before writing, or require cleared memory:

```c
size_t n = 50;
mulle_calloca_do(buf, double, n)    // buf is a `double *`
{
    /* All elements are already 0.0 – safe to read without overwriting */
    double sum = 0.0;
    for (size_t i = 0; i < n; ++i)
        sum += buf[i];              // Reading uninitialized memory would be safe here
    printf("Sum: %f\n", sum);
}

```

*The stack buffer is declared as `{ 0 }`, and any heap fallback uses `mulle_calloc`.*

### Dynamic Reallocation Within the Block

Both macros support resizing if you underestimate your needs:

```c
size_t n = 20;
mulle_alloca_do(buf, char, n)
{
    strcpy(buf, "Hello");
    /* Need more space for concatenation */
    mulle_alloca_do_realloc(buf, 100);  // Moves to heap if necessary
    strcat(buf, " World!");
    puts(buf);
}

```

If the original allocation was on the stack, `mulle_alloca_do_realloc` automatically migrates the contents to a heap block and updates the pointer.

## Related Helper Macros

The [`src/mulle-alloca.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-alloca.h) header provides additional utilities that work with both allocation types:

- **`mulle_alloca_do_realloc` / `mulle_calloca_do_realloc`**: Resize the buffer, transitioning from stack to heap if the new size exceeds `MULLE_ALLOCA_STACKSIZE`
- **`mulle_alloca_do_extract` / `mulle_calloca_do_extract`**: Move ownership of the buffer out of the block, returning a heap pointer that the caller must `mulle_free`
- **`mulle_alloca_do_for` / `mulle_calloca_do_for`**: Provide a convenient iterator pattern over the allocated elements

## Summary

- Both `mulle_alloca_do` and `mulle_calloca_do` prefer **stack allocation** (128 bytes by default) but automatically fall back to the heap for larger requests
- **`mulle_alloca_do`** uses `mulle_malloc` and leaves memory **uninitialized**—requires immediate writing
- **`mulle_calloca_do`** uses `mulle_calloc` and guarantees **zero-initialized** memory via `{ 0 }` stack declarations
- Both automatically free heap memory when the enclosing block exits; stack memory requires no cleanup
- Use **`mulle_alloca_do_realloc`** to dynamically grow buffers that initially fit on the stack

## Frequently Asked Questions

### When should I use mulle_calloca_do instead of mulle_alloca_do?

Use `mulle_calloca_do` when you need to read from the buffer before writing to it, or when you require zero-initialized data structures. According to the source code in [`src/mulle-alloca.h`](https://github.com/mulle-c/mulle-allocator/blob/main/src/mulle-alloca.h), this macro ensures the stack buffer is declared with `{ 0 }` and uses `mulle_calloc` for heap fallbacks, guaranteeing all bytes start as zero.

### What happens if the requested size exceeds the stack buffer?

Both macros automatically allocate on the heap using their respective allocators. `mulle_alloca_do` calls `mulle_malloc` while `mulle_calloca_do` calls `mulle_calloc`. The memory is automatically freed when execution leaves the `for` loop block via the `_mulle_alloca_do_free` helper, which checks whether the pointer references the stack buffer or a heap allocation.

### Can I resize a buffer allocated with mulle_alloca_do?

Yes. The macro `mulle_alloca_do_realloc` (and its `calloca` counterpart) allows you to resize the buffer within the block. If the original allocation was on the stack and the new size exceeds `MULLE_ALLOCA_STACKSIZE`, the helper automatically copies the data to a new heap block and updates your pointer variable.

### Is there a performance difference between the two macros?

For allocations that fit on the stack (≤128 bytes), performance is identical. For heap fallbacks, `mulle_calloca_do` incurs the additional cost of zero-initialization that `mulle_calloc` provides versus `mulle_malloc`. If you will immediately overwrite all memory, `mulle_alloca_do` avoids this unnecessary initialization overhead.