9

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?

halfer
  • 19,824
  • 17
  • 99
  • 186
Phridge
  • 148
  • 8
  • 1
    The [manpage of realloc](http://man7.org/linux/man-pages/man3/realloc.3p.html) mentions what happens if the pointer was not returned by `malloc`, `calloc` or `realloc` earlier: "the behaviour is undefined" – Gerhardh May 31 '19 at 10:25
  • 2
    To use local stack memory is generally discouraged. `alloca()` itself is considered unsafe. Consider that the space available depends from the number of nested routines, and memory violations are always fatal. Moreover this memory will not be available when you leave the local function. Heap allocation strategy isn't a problem if you want to use only **locally** the memory (each DLL will have a local heap), if you want to use it globally the method is wrong for the reason express before. – Frankie_C May 31 '19 at 10:34

2 Answers2

11

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;
    …
}
Gilles 'SO- stop being evil'
  • 104,111
  • 38
  • 209
  • 254
  • Your diagram isn't quite accurate. `a` itself is a pointer, probably occupying 32 or 64 bits on the stack. The object allocated by `alloca` is anonymous, and has a size of 160 bytes (assuming `sizeof (float) == 4`). – Keith Thompson Jun 01 '19 at 09:34
  • −1 That stack layout is not correct. – Cheers and hth. - Alf May 22 '23 at 16:14
  • @Cheersandhth.-Alf Would you mind expanding on what's wrong with it? It's not illustrating a specific architecture, just a conceptual model, so I don't know what you'd be evaluating correctness against. – Gilles 'SO- stop being evil' May 22 '23 at 16:37
  • @Gilles'SO-stopbeingevil': The main problem is that you've placed the allocated array in between other stuff. It isn't: the pointer `a` is there, but the allocated array that it points is at the open end of the stack, the stack "top" in your terminology. All the *names* that the compiler knows about are at fixed offsets in the stack frame, none are moved around by `alloca`. This invalidates the logic in the answer, which is Just Wrong™. – Cheers and hth. - Alf May 23 '23 at 17:51
  • @Cheersandhth.-Alf Oh, right, I merged `a` and what it points to in that diagram, thanks. (And I just noticed Keith's comment was about that as well.) In this simplified example, `a` would probably end up at the top with most compilers, but it can't be guaranteed at least in the case where there are more than one `alloca` blocks in the same function. So showing `k` after `a[]` isn't misleading, even if it's perhaps unrealistic. – Gilles 'SO- stop being evil' May 23 '23 at 18:21
  • k after a[] is **impossible**. k is at a fixed offset in the stack frame while the a[] array is of size established at run time. Please delete this answer. – Cheers and hth. - Alf May 23 '23 at 19:28
  • @Cheersandhth.-Alf “k is at a fixed offset in the stack frame” Why? I'm sure it is with typical compilers, but I don't see why it would _have_ to be. And requesting the deletion of an answer over a minor unrealistic simplification in a diagram is ridiculous. – Gilles 'SO- stop being evil' May 23 '23 at 19:30
  • Your answer is "No: that wouldn't work with a stack as commonly implemented". That is not "minor" but entirely wrong. I've flagged it for moderator attention. – Cheers and hth. - Alf May 23 '23 at 19:35
3

The accepted answer has correctly pointed out that usually there is not enough benefit from realloca because allocations are difficult to "grow".

Another issue I see is that these allocations have a life time till the end of the function. What happens when you pass this pointer to another function and call realloca on it there? This function would not be able to change the stack frame of a function deeper on the stack. It also cannot reallocate it in its own frame because the object would be destroyed when it returns, whereas the original object would still have to be alive.

This problem is not there for malloc/realloc because the heap has a global lifetime.

One could argue that the semantics can be defined in such way that a function can be realloced only in the function it was alloc'd in. This greatly reduces the use such a function would have.

Ajay Brahmakshatriya
  • 8,993
  • 3
  • 26
  • 49