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?
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;
…
}