Reputation: 10197
Let's say we have a struct ending with a flexible array member:
struct foo {
size_t len;
uint8_t data[];
};
How to allocate this struct on the stack (ie. memory is automatically released at the end of the scope)? In add, it would be nice if len
could contain the size of the field data
.
Currently, I do things like:
uint8_t buf[256];
struct foo *foo = (struct foo *)buf;
foo->len = sizeof(buf) - sizeof(struct foo);
However, it is error prone. Use of alloca()
may be slightly better:
struct foo *foo = alloca(256 + sizeof(struct foo));
foo->len = 256;
From there, I could define a macro like this:
#define STACK_ALLOC_FOO(SIZE) ({ \
struct foo *_tmp = alloca(SIZE + sizeof(struct foo)); \
_tmp->len = SIZE; \
_tmp; \
})
And declare it with:
struct foo *foo = STACK_ALLOC_FOO(256);
However, I am not sure of lifetime of the memory allocated with alloca()
. Is it the inner scope or the function?
In add, it does not work to allocate a global variable (even if it is not my main concern).
Does someone has good practices in mind to allocate structures with flexible array members on stack?
Upvotes: 9
Views: 1529
Reputation: 10197
As alternative, I suggest that:
#define DECLARE_FOO(NAME, SIZE) \
struct { \
struct foo __foo; \
char __data[SIZE]; \
} __ ## NAME = { \
.__foo.len = SIZE, \
}; \
struct foo *NAME = &(__ ## NAME).__foo;
So you can do:
DECLARE_FOO(var, 100);
It is not very elegant. However, it works to declare global/static variables and it does not rely on any cast operator.
Upvotes: 1
Reputation: 213563
Let's say we have a struct ending with a Variable Length Array (VLA):
Well, you don't. You have a struct ending with a flexible array member. Different thing and mainly used for dynamic memory allocation scenarios.
How to allocate this struct on the stack
It's pretty hard to do that without some non-standard extension. For example an alloca
extension that guarantees to return memory which does not have an effective type. Meaning that the memory has not yet been marked internally by the compiler to have a certain type. Otherwise...
struct foo *foo = (struct foo *)buf;
You get strict aliasing violation undefined behavior, like in the above buggy code. What is the strict aliasing rule?
Additionally, you also need to take care of alignment & padding.
However, I am not sure of lifetime of the memory allocated with alloca(). Is it the inner scope or the function?
Yeah probably. It's not a standard function and I'm not sure any lib gives a portable guarantee of its behavior. It's not even a POSIX function. Linux man
gives the guarantee that:
The alloca() function allocates size bytes of space in the stack frame of the caller. This temporary space is automatically freed when the function that called alloca() returns to its caller.
I'm assuming this holds for gcc/glibc under *nix, but not other tool chains or systems.
What you could do instead, to get portable and rugged code, is something like this:
struct foo {
size_t len;
uint8_t data[];
};
struct bar256 {
size_t len;
uint8_t data[256];
};
typedef union
{
struct foo f;
struct bar256 b;
} foobar256;
Here bar256
and foobar256
could be defined locally. You can access the data either through the f.data
or b.data
of a foobar256
. This kind of type punning is allowed and well-defined in C.
At this point you might realize that the struct is just more trouble that it's worth and just go with two local variables, one being an actual VLA:
size_t len = ... ;
uint8_t data[len];
Upvotes: 7
Reputation: 22374
If your intent is to use it like this:
#include <stdio.h>
#include <stdlib.h>
#include <alloca.h>
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
struct foo {
size_t len;
uint8_t data[];
};
#define STACK_ALLOC_FOO(SIZE) ({ \
struct foo *_tmp = alloca(SIZE + sizeof(struct foo)); \
_tmp->len = SIZE; \
_tmp; \
})
void print_foo() {
struct foo *h = STACK_ALLOC_FOO(sizeof("Hello World"));
memcpy(h->data, "Hello World", h->len);
fprintf(stderr, "[%lu]%s\n", h->len, h->data);
}
int main(int argc, char *argv[])
{
print_foo();
return 0;
}
Because of this:
The space allocated by alloca() is not automatically deallocated if the pointer that refers to it simply goes out of scope.
it will produce perfectly valid code because the only thing that is going out of scope is the *_tmp
and that does NOT deallocate the alloca, you are still within the same stack-frame. it DOES get de-allocated with the return from print_foo.
Actually it's very interesting to see how the compiler deals with the optimization flags and the assembly output. (The alloca related code is fully duplicated in main
if you e.g. use -O3)
Hopefully that helps
Upvotes: 0
Reputation: 140960
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.
Upvotes: -1
Reputation: 2093
Variable length arrays (as understood in GNU C) are generally not allocated using alloca
. In C90 they are not supported.
The typical way is this:
int main() {
int n;
struct foo {
char a;
int b[n]; // n needs to be in the same scope as the struct definition
};
n = 1;
struct foo a;
a.a = 'a';
a.b[0] = 0;
// writing a.b[1] = 1 will not cause the compiler to complain
n = 2;
struct foo b;
b.a = 'b';
b.b[0] = 0;
b.b[1] = 1;
}
Using -fsanitize=undefined
with GCC (more specifically -fsanitize=bounds
) will trigger a runtime error on accessing an out-of-bounds VLA member.
Upvotes: 0