在 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
的一部分;只有指向它们的指针是。 (顺便说一句,这与固定大小的数组不同,固定大小的数组的大小隐含在其初始值设定项中;这些仅适用于独立类型,但不适用于 struct
或 union
成员的类型)。
编辑:
正如 ShafikYaghmour 评论的那样,C99 灵活阵列是一种可能的替代方案。这些与作为指向动态分配数组的指针的 struct
元素相似但不完全相同。但是,在那种情况下,您不仅不能静态声明数组元素,也不能静态声明 struct
本身的实例,因此这根本无法解决您的初始化程序问题。还有其他一些怪癖和限制。就个人而言,我认为灵活数组没有什么优势,但它们确实使正确释放结构实例变得更容易一些。
我有一个看起来像这样的结构。
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
的一部分;只有指向它们的指针是。 (顺便说一句,这与固定大小的数组不同,固定大小的数组的大小隐含在其初始值设定项中;这些仅适用于独立类型,但不适用于 struct
或 union
成员的类型)。
编辑:
正如 ShafikYaghmour 评论的那样,C99 灵活阵列是一种可能的替代方案。这些与作为指向动态分配数组的指针的 struct
元素相似但不完全相同。但是,在那种情况下,您不仅不能静态声明数组元素,也不能静态声明 struct
本身的实例,因此这根本无法解决您的初始化程序问题。还有其他一些怪癖和限制。就个人而言,我认为灵活数组没有什么优势,但它们确实使正确释放结构实例变得更容易一些。