c - 如何在堆栈上分配具有灵活数组成员的结构
问题描述
假设我们有一个以灵活数组成员结尾的结构:
struct foo {
size_t len;
uint8_t data[];
};
如何在堆栈上分配这个结构(即内存在作用域结束时自动释放)?len
另外,如果可以包含字段的大小,那就太好了data
。
目前,我做的事情如下:
uint8_t buf[256];
struct foo *foo = (struct foo *)buf;
foo->len = sizeof(buf) - sizeof(struct foo);
但是,它容易出错。使用alloca()
可能会稍微好一点:
struct foo *foo = alloca(256 + sizeof(struct foo));
foo->len = 256;
从那里,我可以定义一个这样的宏:
#define STACK_ALLOC_FOO(SIZE) ({ \
struct foo *_tmp = alloca(SIZE + sizeof(struct foo)); \
_tmp->len = SIZE; \
_tmp; \
})
并声明它:
struct foo *foo = STACK_ALLOC_FOO(256);
但是,我不确定分配给alloca()
. 是内部作用域还是函数?
另外,分配全局变量是行不通的(即使这不是我主要关心的问题)。
有人有好的做法在堆栈上分配具有灵活数组成员的结构吗?
解决方案
假设我们有一个以可变长度数组 (VLA) 结尾的结构:
好吧,你没有。您有一个以灵活数组成员结尾的结构。不同的东西,主要用于动态内存分配场景。
如何在堆栈上分配这个结构
如果没有一些非标准扩展,很难做到这一点。例如alloca
,保证返回没有有效类型的内存的扩展。这意味着内存尚未被编译器内部标记为具有某种类型。否则...
结构 foo *foo = (结构 foo *)buf;
你会得到严格的别名冲突未定义行为,就像上面的错误代码一样。什么是严格的别名规则?
此外,您还需要注意对齐和填充。
但是,我不确定使用 alloca() 分配的内存的生命周期。是内部作用域还是函数?
可能是的。这不是一个标准功能,我不确定任何库都可以为其行为提供可移植的保证。它甚至不是 POSIX 函数。Linuxman
保证:
alloca() 函数在调用者的堆栈帧中分配 size 个字节的空间。当调用 alloca() 的函数返回给它的调用者时,这个临时空间会自动释放。
我假设这适用于 *nix 下的 gcc/glibc,但不适用于其他工具链或系统。
相反,您可以做的是获得可移植且坚固的代码,如下所示:
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;
这里bar256
和foobar256
可以在本地定义。您可以通过f.data
或访问数据b.data
。foobar256
这种类型的双关语是允许的,并且在 C 中定义良好。
在这一点上,您可能会意识到结构更麻烦的是它的价值,只需使用两个局部变量,一个是实际的 VLA:
size_t len = ... ;
uint8_t data[len];