cmemory-managementreallocalloca

Can alloca() memory be reallocated?


Memory allocated by malloc can be reallocated with realloc. Is there a similar function for alloca? Reallocating stack memory could be useful when you don't want memory to be allocated on the heap, and you need to allocate variable stack memory multiple times, for example in a library function, where you need dynamic memory, but don't want to allocate on the heap, because the user of the library might use a custom heap allocation strategy. It would look like this:

int main(void) {
    float * some_mem = alloca(40 * sizeof(float));
    // do something with this memory...

    // now we need a different amount of memory, but some_mem still occupies a lot of the stack, so just reallocate it.

    // is something like this possible?
    some_mem = realloca(some_mem, 50 * sizeof(float));
}

The important thing is that this all happens on the stack. Q: is there a way to reallocate dynamic stack memory?


Solution

  • No: that wouldn't work with a stack as commonly implemented. A variable on the stack occupies a fixed range of addresses. The next variable comes immediately after it, so there's no room to grow. Consider a function like this:

    void f(int x) {
        int i;
        float *a = alloca(40 * sizeof(float));
        int k;
        …
    }
    

    The stack after the function prologue looks something like this:

    ----------------+-----+-----+-----+-----+-------------------+-----+---------------------
    ...             | ret | x   | i   | a   | a[]               | k   | ...                 
    ----------------+-----+-----+-----+-----+-------------------+-----+---------------------
    ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
    previous frames                       f's frame                    free space at the top
    

    There's no room to grow a.

    I'm showing a highly simplified example: in the real world, variables end up in registers, variables can be reordered even if they do end up on the stack, etc. But only one variable can be the last one on the stack with room to grow.

    So if realloca existed, it could only be applied to the variable that's at the top of the stack. (Or else it would have to move everything else that's on top of it, but that would require updating all existing pointers to those, which is not possible in general.) This would be a very limited mechanism, so support for this feature would have a very small benefit. Supporting it would have a significant cost, because compilers are normally free to put things on the stack in the order they want: this feature would require a new mechanism to let the compiler know that one specific variable must go to the top.

    It's possible that some C implementation somewhere has realloca, but it's unlikely given the cost/benefit ratio.

    Of course realloca can easily be implemented if alloca does not use a stack allocation strategy. But allocating on the stack is the whole point of alloca. If you want resizable objects, you need a memory management structure with a heap interface, and that's what malloc is for.


    As a practical matter, there are several possible approaches to dynamic memory management in a library.

    The most common approach is to call malloc, realloc and free when you need them. That's what they're for.

    In some environments, it's useful to support custom allocators. You can give the user of the library the option to pass pointers to alternative implementations of malloc, realloc and free. It's useful when you want to write a portable library that needs to be used by code that is itself fully portable. Most of the time, though, users who want to use custom allocators can do it by linking their own malloc and friends. And even that is rarely useful.

    If you need code that can work in an environment without dynamic allocation (such as safety-critical environments), then you should not use alloca either. alloca is worse than malloc because it causes unpredictable stack usage and can lead to a stack overflow which won't be detected at all, or which will only be detected by a program crash. If you need a variable (or large) amount of temporary memory in a function, have the user pass a suitably-sized buffer to you.

    /** [documentation of the function] …
     * working_buffer must point to an array of floats of 3*n elements.
     */
    void f(size_t n, float *working_buffer);
    

    Better, if you have the code size budget, pass the array size and verify it.

    /** [documentation of the function] …
     * working_buffer must point to an array of floats of 3*n elements.  
     */
    int f(size_t n, float *working_buffer, size_t working_buffer_length)
    {
        if (working_buffer_length < 3 * n) return -EINVAL;
        …
    }