在 C 中声明具有可变大小的全局结构变量

Declare Global Struct Variable With Variable Size In C

我有一个看起来像这样的结构。

struct MyStruct1 {
    int (*fn)();
    int b;
}

还有另一个看起来像这样的结构。

struct MyStruct2 {
    int a;
    struct MyStruct1 b[0];
}

我想声明一个 MyStruct2 类型的全局变量,有点像这样。

int func1() { return 1; }
int func2() { return 2; }
struct MyStruct2 a = { 1, { func1, 5 }, { func2, 6 } };

但是,我得到了 "Initializer Element is not a compile-time constant"。

我想知道 (a) 是否有可能全局声明一个可变大小的结构(或者至少定义一个大小正确的空间块,以便稍后插入值),以及(b) 如果是,我做错了什么?

您不能在 C 中合法地创建大小为 0 的数组。在 C99 或 C11 中,您可以像这样使用 'flexible array member':

struct MyStruct2 {
    int a;
    struct MyStruct1 b[];
};

但是只有使用动态内存分配才能创建具有灵活数组成员的结构(其他形式的分配会给您一个不可用的大小为 0 的灵活数组)。

具有可变大小数组的结构的旧 'struct hack' 版本在结构中使用大小为 1 的数组。您可以使用大小为 1 的数组创建此类结构的全局版本。

但基本上,您是在尝试做语言禁止您做的事情,结果您失败了也就不足为奇了。

您对此的处理取决于您的需要。全局变量本质上有些不可取,所以有一个元素"you should be trying to avoid doing this"。也就是说,这些规则也适用于文件范围 (static) 变量,而且这些变量有很多用途。

您可以使用显式指针代替数组,并单独分配 struct MyStruct2 的主体及其 struct MyStruct1 成员的数组。您可以放弃全局变量并使用具有灵活数组成员的动态分配结构。

struct MyStruct2 *ms2 = malloc(sizeof(*ms2) + N * sizeof(ms2->b[0]));

这将创建一个 struct MyStruct2(如本答案顶部所示),数组中有 N 个成员。无需任何进一步更改,您可以使用 ms2->b[0]ms2->b[N-1](好吧,除了错误检查 malloc() 成功)。

无法在本地或全局声明可变大小 struct。每种类型都有在编译时确定的固定大小。

但是,您报告的错误消息令人惊讶。如果你给出的所有声明都在文件范围内,在同一个文件中,按照你给它们的顺序,那么变量 a 的初始化程序是 编译时常量。但是,它不是 struct MyStruct2

的正确初始化器
  • 因为它指定的元素多于 struct 类型的成员,
  • 因为 a.b 的初始值设定项是 struct MyStruct1 的初始值设定项,而不是此类数组的初始值设定项,并且
  • 因为即使你将最后两个初始化器元素转换为一个数组初始化器,它的元素也比 a.b 中的元素多(即多于零)。

如果你想要一个动态大小的数组,无论是本身作为变量还是作为 struct 的成员,那么你必须声明一个指向它的指针,并为元素动态分配内存.在那种情况下,元素本身不是 struct 的一部分;只有指向它们的指针是。 (顺便说一句,这与固定大小的数组不同,固定大小的数组的大小隐含在其初始值设定项中;这些仅适用于独立类型,但不适用于 structunion 成员的类型)。

编辑: 正如 ShafikYaghmour 评论的那样,C99 灵活阵列是一种可能的替代方案。这些与作为指向动态分配数组的指针的 struct 元素相似但不完全相同。但是,在那种情况下,您不仅不能静态声明数组元素,也不能静态声明 struct 本身的实例,因此这根本无法解决您的初始化程序问题。还有其他一些怪癖和限制。就个人而言,我认为灵活数组没有什么优势,但它们确实使正确释放结构实例变得更容易一些。