为什么这种具有灵活数组成员的结构初始化无效但对固定大小的数组成员有效?

Why is this initialization of a structure with a flexible array member invalid but valid with an fixed size array member?

C 标准声明(强调我的):

21 EXAMPLE 2 After the declaration:

struct s { int n; double d[]; };

the structure struct s has a flexible array member d. [...]


22 Following the above declaration:

struct s t1 = { 0 };         // valid
struct s t2 = { 1, { 4.2 }}; // invalid
t1.n = 4;                    // valid
t1.d[0] = 4.2;               // might be undefined behavior

The initialization of t2 is invalid (and violates a constraint) because struct s is treated as if it did not contain member d.

Source: C18, §6.7.2.1/20 + /21

我不明白“因为struct s被视为不包含成员d”的解释

如果我使用{ 1, { 4.2 }};的初始化器,{ 4.2 }部分是初始化灵活数组成员; 准确地说,将 灵活数组成员 初始化为由一个元素组成,并将此元素初始化为值 4.2,因此 stuct s 被视为具有成员d要不要?

这句话在我看来毫无意义。

如果我使用固定大小的数组,此表示法有效并毫无怨言地初始化成员:

struct foo {
    int x;
    double y[1];
};

int main (void)
{
    struct foo a = { 1, { 2.3 } };
}

Evidence

你能详细说明一下吗?


我读过:

How to initialize a structure with flexible array member

和其他人,但其中 none 回答了我这句话想要解释的内容以及为什么这是无效的。


相关:

I do not understand the explanation of "because struct s is treated as if it did not contain member d".

C 标准也说“在大多数情况下,可变数组成员被忽略。”不清楚你为什么不明白这是什么意思。如果 struct s 被声明为 struct s { int n; double d[]; };,那么在大多数情况下,C 实现的行为就像它被声明为 struct s { int n; }; 一样。因此,struct s t2 = { 1, { 4.2 }}; 失败,因为 4.2 是实际上不存在的东西的初始值设定项。

明智的做法是问为什么会出现这种情况。在大多数情况下,我希望编译器能够支持这样一种定义,即计算数组初始值设定项并用于设置结构大小。当然,编译器会使用 s int a[] = { 3, 4, 5}; 等数组定义来执行此操作。但是,这不是灵活数组成员的典型用例。通常,程序会收到有关需要使用结构管理多少元素的信息,为结构分配 space 并为包含的这些元素分配 space,然后将结构放入分配的 space.也就是说,具有灵活数组成员的结构的典型用例是动态分配的 space。我希望 C 委员会认为没有必要要求编译器支持静态或自动对象中的灵活数组成员,而不是动态对象。

我猜这是语言缺陷。虽然初始化一个灵活的数组成员可能没有意义,但标准需要在某个地方解决这个问题。我到处都找不到这样的规范文本。

灵活数组成员的定义是,C17 6.7.2.1/18:

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.

由此得知灵活的数组成员是一种不完整的数组类型。然而,我们不知道在什么情况下灵活数组成员被忽略,除了计算结构大小时。 “在大多数情况下”没有帮助而且是缺陷——这需要扩展到一个详尽的列表,包括初始化列表的一部分时灵活数组成员的行为。否则,人们可能会认为它的行为就像任何其他不完整类型的数组一样。

C17 6.2.5/22:

An array type of unknown size is an incomplete type.

然后初始化规则说,C17 6.7.9:

The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.

到目前为止,还没有规范文本说我们不允许为灵活的数组成员提供初始化器——恰恰相反。问题中的示例(C17 6.7.2.1 示例 21)不是规范的,因为示例在 ISO 标准中不是规范的。该示例没有提及违反了哪个约束,也没有提及必须忽略灵活数组成员的位置。

我想我可能会就此提交 DR。

IMO 这是因为当结构声明为 extern.

时,无法在另一个编译单元中确定以这种方式初始化的结构的 sizeof

您在引用的示例中省略了一些重要的语言 - 这是全文:

20 EXAMPLE 2 After the declaration:

struct s { int n; double d[]; };
the structure struct s has a flexible array member d. A typical way to use this is:

int m = /* some value */;
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));
and assuming that the call to malloc succeeds, the object pointed to by p behaves, for most purposes, as if p had been declared as:

struct { int n; double d[m]; } *p;
(there are circumstances in which this equivalence is broken; in particular, the offsets of member d might not be the same).

IOW,灵活的数组成员只有在动态分配 struct 实例并为数组成员分配额外的 space 时才真正发挥作用。

灵活数组成员没有大小,因此它不会影响 struct 类型的大小 - 也就是说,sizeof (struct s) 的结果计算为类型 没有数组。