How does an array of structures with flexible arra

2020-04-07 04:57发布

As the title states I was wondering how arrays of C-structs with a flexible array member behaves. Here is an example:

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

The Wikipedia article says:

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

On my machine this corresponds to 8 bytes (sizeof(size_t)). But what happens, when I do the following:

Obviously the array cannot hold the data of vector v0, since it's only 3*8 bytes = 24 bytes wide. How can I deal with situations like this?

#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;
}

For example, when I'm writing a library (header: mylib.h, source: my lib.c) and want to hide the implementation of one specific struct from the user (struct declared in header, defined in source - hidden). Sadly exactly this struct contains a flexible array member. Won't this lead to unexpected behavior, when the user tries to create an array of named structures?

Extra: Read more about the flexible array in the OpenSTD C Spec.
Just search for 'flexible array member'.

EDIT: The latest draft of the C11 Standard, the most up to date freely available reference for the C language is available here: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

2条回答
萌系小妹纸
2楼-- · 2020-04-07 05:36

Structures with a flexible array as their last member cannot be used as members of other structures or as array elements. In such constructions, the flexible array cannot be used as it has a size of 0 elements. The C Standard quoted by Jonathan Leffler is explicit, although the language used is quite technical and the paragraphs cannot be found in the Standard by searching for flexible.

The compiler should have issued an error for your array of struct vector.

In your program, you should instead use an array of pointers to struct vectors, each pointing to an object allocated for the appropriate number of elements in the its flexible array.

Here is a modified version:

#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;
}
查看更多
放荡不羁爱自由
3楼-- · 2020-04-07 05:38

You can't have arrays of structures with flexible array members.

The C standard, ISO/IEC 9899:2011, says:

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.

Emphasis added — the italic part of that prohibits arrays of structures with flexible array members. You can have arrays of pointers to such structures, though, but each structure will be separately allocated.

¶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.

This defines a flexible array member.

If you think about it, it makes sense. Pointer arithmetic and arrays rely on all the objects in the array being the same size (hence the equivalence of a[i] == *(a + i), etc), so having an array of objects of varying size would break pointer arithmetic. An array of pointers isn't a problem because the pointers are all the same size, even if the objects pointed at are of different sizes.

If you manage to get a compiler to ignore the violated constraint, then each element of the array will have a zero length flexible array member because the structures will be treated as having the size of the structure without the array member (that's the 'in most situations, the flexible array member is ignored' rule at work). But the compiler should reject an array of a structure type with a flexible array member; such code is violating a constraint (¶3 is in the constraints section; ¶18 is in the semantics section).

查看更多
登录 后发表回答