When is alloca() preferable to memory allocated on the stack by declaring a fixed size array?
Details:
As we know, alloca() is a controversial function. Used recklessly, it can cause stack overflow. Used judiciously, it can shave a few nanoseconds from a tight loop by avoiding heap allocation. In this question about why alloca is considered bad, several of the top answers advocate for the occasional use of alloca.
Another way to allocate from the stack is to simply declare a fixed size array. An example of this strategy can be found in the arena class in Howard Hinnant's stack allocator. (That code is of course C++ but the concept is still applicable to C.)
What are the tradeoffs of using alloca vs a fixed size array? When, if ever, is one clearly preferable to the other? Is it simply a question of performance that should be empirically tested in each individual situation (when performance is a key goal and a hotspot has already been identified)? The fixed size array is more pessimistic -- it always allocates as much as we're willing to allocate on the stack -- but it's not clear whether this is good or bad.
Just to be as clear as possible, here's a very simple example of two function implementations where it seems reason to use either alloca or a fixed size array:
#include <alloca.h>
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
void foo_alloca(const size_t mem_needed) {
printf("foo_alloca(%zu)\n", mem_needed);
char* mem;
bool used_malloc = false;
if (mem_needed <= 100)
mem = alloca(mem_needed);
else {
mem = malloc(mem_needed);
used_malloc = true;
}
assert(mem_needed != 0);
// imagine we do something interesting with mem here
mem[0] = 'a';
mem[1] = 'b';
mem[2] = 'c';
mem[3] = '\0';
puts(mem);
if (used_malloc)
free(mem);
}
void foo_fixed(const size_t mem_needed) {
printf("foo_fixed(%zu)\n", mem_needed);
char* mem;
char stack_mem[100];
bool used_malloc = false;
if (mem_needed <= 100)
mem = stack_mem;
else {
mem = malloc(mem_needed);
used_malloc = true;
}
assert(mem_needed != 0);
// imagine we do something interesting with mem here
mem[0] = 'a';
mem[1] = 'b';
mem[2] = 'c';
mem[3] = '\0';
puts(mem);
if (used_malloc)
free(mem);
}
int main()
{
foo_alloca(30);
foo_fixed(30);
foo_alloca(120);
foo_fixed(120);
}
Another option very similar to alloca is VLAs. As far as I know, memory obtained from alloca and VLAs have essentially the same behavior so the question applies to VLAs as well. If that understanding is wrong just mention it.