关于C#:为什么使用灵活的数组成员进行的结构初始化无效,但对于固定大小的数组成员却有效?

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:

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

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

22 Following the above declaration:

1
2
3
4
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的成员?

在我眼里这句话毫无意义。

  • 为什么标准会说{ 4.2 }不会初始化/表示灵活数组成员,因此该结构将被视为没有成员d

如果我使用固定大小的数组,此表示法将起作用并初始化成员,而不会产生任何抱怨:

1
2
3
4
5
6
7
8
9
struct foo {
    int x;
    double y[1];
};

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

证据

  • 为什么在结构具有柔性数组成员时此初始化无效,而在结构具有固定大小数组成员时有效?

您能详细说明吗?

我读过:

为什么灵活数组成员的静态初始化有效?

如何使用灵活的数组成员初始化结构

灵活的数组成员会导致未定义的行为吗?

和其他人,但没有一个回答我这句话想解释什么以及为什么这实际上是无效的。

相关:

  • 具有灵活数组成员的结构的数组如何表现?
  • 灵活阵列成员的真正好处是什么?

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

柔性数组成员的定义是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。


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};这样的数组定义来执行此操作。但是,这不是灵活数组成员的典型用例。通常,程序会接收有关需要在结构中管理多少个元素的信息,为结构分配空间并为包含的那些元素分配空间,然后将结构放入分配的空间中。也就是说,具有灵活数组成员的结构的典型用例是动态分配空间。我希望C委员会几乎不需要要求编译器在静态或自动对象(而不是动态对象)中支持灵活的数组成员。


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

20 EXAMPLE 2 After the declaration:

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

the structure struct s has a flexible array member d. A typical way to use this is:

1
2
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:

1
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实例并为该数组成员分配额外空间的情况下,灵活的数组成员才真正发挥作用。

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


IMO,这是因为当将结构声明为extern时,无法在另一个编译单元中确定以这种方式初始化的结构的sizeof