我正在使用最新版本的 g++ 编译一些涉及结构指针数组的测试代码,其中结构包含一个数组。当我尝试在一行中初始化结构指针数组时,编译失败,但如果我将结构分配给变量,然后在创建数组时获取它们的地址,g++ 不会报错。我想了解原因。这是我的第一篇文章,所以如果有遗漏的信息,我深表歉意。
我搜索了类似的问题,但没有找到任何具体的内容。
#include <stdint.h>
typedef struct {
uint16_t num_elements;
uint8_t elements[];
} HasArray;
HasArray x = {.num_elements = 2, .elements = {51, 17}};
HasArray y = {.num_elements = 4, .elements = {1, 42, 88, 73}};
const HasArray* collection[] = {&x, &y};
// main() somewhere
上述代码可以工作,我认为这是因为 g++ 可以在编译时为x
和分配内存y
(或类似情况)。如果我尝试collection
像这样初始化:
// HasArray cast is necessary so I can take the address, doing `&` on just the `{}`
// didn't work
// Also I would use a macro for these declarations, I know it's clunky
const HasArray* collection[] = {
&(HasArray){.num_elements = 2, .elements = {51, 17}},
&(HasArray){.num_elements = 4, .elements = {1, 42, 88, 73}}
};
g++ 抛出
test.c:16:56: error: non-static initialization of a flexible array member
16 | &(HasArray){.num_elements = 2, .elements = {51, 17}},
| ^
test.c:17:63: error: non-static initialization of a flexible array member
17 | &(HasArray){.num_elements = 4, .elements = {1, 42, 88, 73}}
| ^
这些情况有什么不同?在我看来,第二种语法仍将在编译时分配内存,但事实似乎并非如此。我知道我可以在运行时使用分配内存malloc()
,但第一种情况不需要它,而且我知道编译时结构的大小,那么如何在不为每个结构创建单独变量的情况下向编译器指示大小?
编辑:我确实搞错了问题,哎呀。这是针对 c 代码的,但我必须使用 g++ 来编译它。@ChrisMM 在评论中提供了一个非常好的答案。为了解决这个错误,我将数组分配给变量并像第一种情况一样获取它们的地址。
首先,初始化灵活数组成员是一种非标准扩展。GCC 允许在具有静态存储持续时间的对象(即在文件范围或使用
static
关键字声明的对象)的情况下这样做,因为它可以在编译时为其留出适当的空间。关于为什么会产生错误:
在 C23 之前,复合文字的存储持续时间存在歧义。特别是在 GCC 中,它们始终具有自动存储持续时间。并且由于 GCC 仅允许对具有静态存储持续时间的对象进行灵活的数组成员初始化,因此您会收到错误。
C23 标准对此进行了澄清,指出复合文字的存储持续时间是封闭范围的存储持续时间。因此,如果您使用具有完整 C23 支持的 GCC 13 或更高版本进行编译,则上述内容将顺利编译。
在标准 C 中,灵活数组成员(灵活数组成员在 C++ 中有效吗?)被定义为具有未指定大小的结构的最后一个元素,允许动态调整大小。
发生这种情况的原因是无法以这种方式初始化灵活数组成员。C 标准要求必须动态分配具有 FAM 的结构来定义灵活数组的大小。不允许对 FAM 进行静态或复合文字初始化。
在您最初的方法中:
一些编译器允许这样做,因为 x 和 y 在编译时定义为已知大小(因为每次 x 和 y 的大小都相同;它不是动态的,例如,具有大小的 scanf 或类似的东西)。但是,这种用法是非标准的,依赖于特定于编译器的扩展。C 标准没有定义以这种方式初始化 FAM 的行为,这使得代码不可移植。
因此,为了符合标准,您必须为具有 FAM 的结构动态分配内存:
因此这将给你: