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

时间:2016-03-20 19:36:22

标签: c arrays struct flexible-array-member

正如标题所述,我想知道具有flexible array member的C结构的数组如何表现。这是一个例子:

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

维基百科的文章说:

  

这样的结构上的sizeof运算符需要给出灵活数组成员的偏移量。

在我的机器上,这对应于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,source:my lib.c)并希望隐藏用户的一个特定结构的实现时(在头文件中声明的结构,定义在源 - 隐藏)。遗憾的是,这个结构包含一个灵活的数组成员。当用户尝试创建一个命名结构数组时,这不会导致意外行为吗?

额外:详细了解OpenSTD C Spec中的灵活数组。
只需搜索'灵活阵列成员'。

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

2 个答案:

答案 0 :(得分:3)

将灵活数组作为最后一个成员的结构不能用作其他结构或数组元素的成员。在这种结构中,不能使用柔性阵列,因为它具有0个元素的大小。 Jonathan Leffler引用的C标准是明确的,尽管使用的语言非常技术性,并且通过搜索灵活在标准中找不到段落。

编译器应该为您的struct vector数组发出错误。

在你的程序中,你应该使用一个指向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;
}

答案 1 :(得分:3)

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

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

  

6.7.2.1结构和联合说明符

     

¶3结构或联合不得包含不完整或功能类型的成员(因此,   结构不应包含自身的实例,但可以包含指向实例的指针   本身),除了具有多个命名成员的结构的最后一个成员   可能有不完整的数组类型;这样的结构(和任何包含的结合,可能   递归地,这种结构的成员不应该是结构的成员或者   数组的元素。

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

  

¶18作为一种特殊情况,具有多个命名成员的结构的最后一个元素可以   有一个不完整的数组类型;这称为灵活数组成员。在大多数情况下,   灵活的数组成员被忽略。特别是,结构的大小就像是   省略了灵活的数组成员,除了它可能有更多的尾随填充   遗漏意味着。但是,当.(或->)运算符具有左操作数时   (指向)具有灵活数组成员和右操作数名称的结构   成员,它的行为好像该成员被替换为最长的数组(具有相同的   元素类型)不会使结构大于被访问的对象;该   数组的偏移量应保持灵活阵列成员的偏移量,即使这会有所不同   从替换阵列的。如果此数组没有元素,则表现得好像   它有一个元素,但如果有任何尝试访问它,行为是未定义的   元素或生成一个经过它的指针。

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

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

如果你设法让编译器忽略违反的约束,那么数组的每个元素将具有零长度的灵活数组成员,因为结构将被视为具有没有数组成员的结构的大小(这是'在大多数情况下,灵活的数组成员被忽略'工作规则'。但是编译器应该拒绝具有灵活数组成员的结构类型的数组;这样的代码违反了约束(¶3在约束部分;¶18在语义部分)。