具有灵活数组成员的结构数组如何表现?

How does an array of structures with flexible array members behave?

如标题所述,我想知道 C-structs 和 flexible array member 的数组的行为方式。这是一个例子:

struct vector {
    size_t length;
    double array[];
};

维基百科文章说:

The sizeof operator on such a struct is required to give the offset of the flexible array member.

在我的机器上,这对应于 8 个字节 (sizeof(size_t))。但是,当我执行以下操作时会发生什么:

显然数组不能容纳向量 v0 的数据,因为它只有 3*8 字节 = 24 字节 宽。我该如何处理这种情况?

#define LENGTH 10

int main() {
    struct vector arr[3];

    struct vector *v0 = calloc(1, sizeof(*v0) + LENGTH * sizeof(v0->array[0]));
    v0->length = LENGTH;

    size_t i;
    for (i = 0; i < v0->length; i++) {
        v0->array[i] = (double) i;
    }

    struct vector v1;
    struct vector v2;

    arr[0] = *v0;
    arr[1] =  v1;
    arr[2] =  v2;

    for (i = 0; i < arr[0].length; i++) {
        printf("arr[0].array[%2zu] equals %2.0lf.\n", i, arr[0].array[i]);
        printf("    v0->data[%2zu] equals %2.0lf.\n", i, v0->array[i]);
    }
    return 0;
}

例如,当我正在编写一个库(header:mylib.h,来源:my lib.c)并想对用户隐藏一个特定结构的实现时(在 header 中声明的结构,在源代码中定义 - 隐藏)。不幸的是,这个结构恰好包含一个灵活的数组成员。当用户尝试创建命名结构数组时,这不会导致意外行为吗?

Extra:阅读 OpenSTD C Spec 中有关灵活数组的更多信息。
只需搜索“灵活数组成员”。

EDIT:C11 标准的最新草案,最新的 C 语言免费参考资料可在此处获得:http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

以灵活数组作为最后一个成员的结构不能用作其他结构的成员或数组元素。在此类结构中,不能使用灵活数组,因为它的大小为 0 个元素。 Jonathan Leffler 引用的 C 标准是明确的,尽管使用的语言非常技术化,并且通过搜索 flexible.

无法在标准中找到这些段落

编译器应该为您的结构向量数组发出错误。

在您的程序中,您应该使用指向 struct vectors 的指针数组,每个指针指向一个对象,该对象分配给其灵活数组中适当数量的元素。

这是修改后的版本:

#include <stdio.h>
#include <stdlib.h>

struct vector {
    size_t length;
    double array[];
};

struct vector *make_vector(size_t n) {
    struct vector *v = malloc(sizeof(*v) + n * sizeof(v->array[0]));
    v->length = n;
    for (size_t i = 0; i < n; i++) {
        v->array[i] = (double)i;
    }
    return v;
}

int main(void) {
    struct vector *arr[3];

    arr[0] = make_vector(10);
    arr[1] = make_vector(5);
    arr[2] = make_vector(20);

    for (size_t n = 0; n < 3; n++) {
        for (size_t i = 0; i < arr[n]->length; i++) {
            printf("arr[%zu]->array[%2zu] equals %2.0lf.\n",
                   n, i, arr[0]->array[i]);
        }
    }
    return 0;
}

您不能拥有具有灵活数组成员的结构数组。

C 标准,ISO/IEC 9899:2011,说:

6.7.2.1 Structure and union specifiers

¶3 A structure or union shall not contain a member with incomplete or function type (hence, a structure shall not contain an instance of itself, but may contain a pointer to an instance of itself), except that the last member of a structure with more than one named member may have incomplete array type; such a structure (and any union containing, possibly recursively, a member that is such a structure) shall not be a member of a structure or an element of an array.

已添加强调 — 斜体部分禁止使用具有灵活数组成员的结构数组。不过,您可以拥有指向此类结构的指针数组,但每个结构将单独分配。

¶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. However, when a . (or ->) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) that would not make the structure larger than the object being accessed; the offset of the array shall remain that of the flexible array member, even if this would differ from that of the replacement array. If this array would have no elements, it behaves as if it had one element but the behavior is undefined if any attempt is made to access that element or to generate a pointer one past it.

这定义了一个灵活的数组成员。

如果你仔细想想,这是有道理的。指针算法和数组依赖于数组中的所有对象都是相同大小的(因此 a[i] == *(a + i) 等价),因此具有不同大小的对象数组会破坏指针算法。指针数组不是问题,因为指针的大小都相同,即使指向的对象大小不同。

如果您设法让编译器忽略违反的约束,则数组的每个元素都将具有零长度的灵活数组成员,因为结构将被视为具有没有数组成员的结构的大小 (这就是工作中的 'in most situations, the flexible array member is ignored' 规则)。但是编译器应该拒绝具有灵活数组成员的结构类型的数组;这样的代码违反了约束(¶3 在约束部分;¶18 在语义部分)。