首页 > 解决方案 > 如何在堆栈上分配具有灵活数组成员的结构

问题描述

假设我们有一个以灵活数组成员结尾的结构:

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(). 是内部作用域还是函数?

另外,分配全局变量是行不通的(即使这不是我主要关心的问题)。

有人有好的做法在堆栈上分配具有灵活数组成员的结构吗?

标签: cflexible-array-member

解决方案


假设我们有一个以可变长度数组 (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;

这里bar256foobar256可以在本地定义。您可以通过f.data或访问数据b.datafoobar256这种类型的双关语是允许的,并且在 C 中定义良好。

在这一点上,您可能会意识到结构更麻烦的是它的价值,只需使用两个局部变量,一个是实际的 VLA:

size_t len = ... ;
uint8_t data[len];

推荐阅读