Jér*_*ler 8 c flexible-array-member
假设我们有一个以灵活数组成员结尾的结构:
struct foo {
size_t len;
uint8_t data[];
};
Run Code Online (Sandbox Code Playgroud)
如何在堆栈上分配这个结构(即内存在作用域结束时自动释放)?另外,如果len能包含字段的大小就更好了data。
目前,我做的事情如下:
uint8_t buf[256];
struct foo *foo = (struct foo *)buf;
foo->len = sizeof(buf) - sizeof(struct foo);
Run Code Online (Sandbox Code Playgroud)
然而,它很容易出错。使用alloca()可能会稍微好一点:
struct foo *foo = alloca(256 + sizeof(struct foo));
foo->len = 256;
Run Code Online (Sandbox Code Playgroud)
从那里,我可以定义一个像这样的宏:
#define STACK_ALLOC_FOO(SIZE) ({ \
struct foo *_tmp = alloca(SIZE + sizeof(struct foo)); \
_tmp->len = SIZE; \
_tmp; \
})
Run Code Online (Sandbox Code Playgroud)
并声明它:
struct foo *foo = STACK_ALLOC_FOO(256);
Run Code Online (Sandbox Code Playgroud)
但是,我不确定分配的内存的生命周期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;
Run Code Online (Sandbox Code Playgroud)
这里bar256和foobar256可以在本地定义。您可以通过 或f.data访问b.data数据foobar256。这种类型双关在 C 中是允许的并且是明确定义的。
此时,您可能会意识到该结构只是更麻烦,值得,只需使用两个局部变量,其中一个是实际的 VLA:
size_t len = ... ;
uint8_t data[len];
Run Code Online (Sandbox Code Playgroud)
可变长度数组(如GNU C中所理解的)通常不使用 进行分配alloca。在 C90 中,它们不受支持。
典型的方式是这样的:
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;
}
Run Code Online (Sandbox Code Playgroud)
-fsanitize=undefined与 GCC(更具体地说)一起使用-fsanitize=bounds将在访问越界 VLA 成员时触发运行时错误。
| 归档时间: |
|
| 查看次数: |
1050 次 |
| 最近记录: |