How to allocate a struct with a Variable Length Array (VLA) on the stack
You have to make sure your buffer is properly aligned. Use unsinged char
or just char
to represent "bytes", uint8_t
represents an 8-bit number.
#include <stdalign.h>
alignas(struct foo) unsigned char buf[sizeof(struct foo) + 20 * sizeof(uint8_t));
struct foo *foo = (struct foo *)buf;
foo->len = sizeof(buf) - sizeof(struct foo);
I could define a macro like this:
The ({
is a gcc extension. You could also define a macro to define the variable, like:
// technically UB I believe
#define FOO_DATA_SIZE sizeof(((struct foo*)0)->data)
struct foo *foo_init(void *buf, size_t bufsize, size_t count) {
struct foo *t = buf;
memset(t, 0, bufsize);
t->size = count;
return t;
}
#define DEF_struct_foo_pnt(NAME, COUNT) \
_Alignas(struct foo) unsigned char _foo_##NAME##_buf[sizeof(struct foo) + COUNT * FOO_DATA_SIZE); \
struct foo *NAME = foo_init(_foo_##NAME##_buf, sizeof(buf), COUNT);
void func() {
DEF_struct_foo_pnt(foo, 20);
}
Use of alloca() may be slightly better:
Unless you happen to call alloca()
in a loop...
I am not sure of lifetime of the memory allocated with alloca(). Is it the inner scope or the function?
Memory allocated with alloca gets freed at end of function or at end of scope?
it does not work to allocate a global variable (even if it is not my main concern).
It will be hard - C does not have constructors. You could use an external tool or experiment with preprocessor magic to generate code, like:
_Alignas(struct foo) unsigned char buf[sizeof(struct foo) + count * sizeof(uint8_t)) = {
// Big endian with 64-bit size_t?
20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
struct foo *foo_at_file_scope = (struct foo*)buf;
i.e. you have to initialize the buffer, not the struct. I think I would write a tool in C using the same compiler with same options to generate code for that (for cross-compiling in gcc-ish environment, I would only compile some executable with initialization to an ELF file, and instead of running it, get the initialization from ELF file with objdump
, and the process it to generate C source).
Alternatively, you could (ab-)use GCC extension __attrbute__((__constructor__))
- define a function with that attribute in another macro. Something along:
#define DEF_FILE_SCOPE_struct_foo_pnt(NAME, COUNT) \
_Alignas(struct foo) unsigned char _foo_##NAME##_buf[sizeof(struct foo) + COUNT * FOO_DATA_SIZE); \
struct foo *NAME = NULL; \
__attribute__((__constructor__)) \
void _foo_##NAME##_init(void) { \
NAME = foo_init(_foo_##NAME##_buf, sizeof(buf), COUNT); \
}
DEF_FILE_SCOPE_struct_foo_pnt(foo_at_file_scope, 20)
Does someone has good practices in mind to allocate [flexible array members] on stack?
Don't use them. Instead, use pointers and malloc.
alloca
is killed when the function returns. which means you have no chance of returningfoo
– RobbierobbinDoes someone has good practices in mind to allocate VLA on stack?
Don't use them. That is not a struct with VLA but a struct with flexible array member. Variable lenght array is not a flexible array member, those are different terms. – Vicentevicepresidentalloca
man page: "This temporary space is automatically freed when the function that called alloca() returns to its caller." – Foppish