Question

I have a question about the flexible-length arrays in C structures (http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html).

typedef struct  {
    size_t N;
    int elems[];
} A_t;

Now the general approach is quite obvious,

A_t * a = malloc(sizeof(A_t) + sizeof(int) * N)
a->N = N;
....

Now this seems to be awkward when trying to incorporate stuff into other structs or stack-based allocation. So something like the following snipet is bound to fail for N!=0

struct {
   A_t a;
   A_t b;    /// !!!!!
   double c; /// !!!!!
};

Now I think it should be possible to allow for usages like this by defining another type

typedef struct  {
    size_t N;
    int elems[5];
} A_5_t;

struct {
   A_5_t a;
   A_5_t b;
   double c; // should work here now.
} mystruct;

and then use it as if it were an A_t structure. When calling a function void foo(A_t * arg1);, one would need to use something like foo((A_t*) (&mystruct.b)). Which -- to me -- appears to be a bit clumsy. I therefore wonder whether there is a better way to do this. I wonder whether one could employ a union type for this somehow?

I am asking this question, because the flexible-length array makes it possible to have data in one piece in the structure, therefore one can copy a struct with a single command instead of having to worry about deep and shallow copies, etc.

Was it helpful?

Solution 3

I have figured it out now (the solution has actually been descibed in the gnu documentation as provided above). By appending an array declaration after the struct declaration, one does create a contiguous memory range that is directly adjacent to the "empty" flexible array. Therefore b.A.elems[i] is referencing the same data as b.elems_[i].

It is probably advisable to choose an identifier that tells you that the memory of this array is actually belonging to the structure. at least thats how I would use it then.

typedef struct  {
    size_t N;
    double elems[];
} A_t;
typedef struct {
    A_t a;
    double elems_[4];
} B_t;
void foo(A_t * arg1) {
    for (size_t i=0; i < arg1->N; ++i) {
        printf("%f\n", arg1->elems[i]);
    }
}
int main(int argc, char *argv[]) {
    B_t b;
    b.a.N = 4;
    for (int i=0; i < 4; ++i) {
        b.elems_[i] = 12.4;
    }
    foo(&b.a);
}

OTHER TIPS

You have a mult-layered question.

In this one example:

struct {
   A_t b;
   double c; /// fails
};

I would try:

 struct {
    double c;
    A_t  b;
 };

Always place the variable portion of a struct at the end. Note, I don't use GCC, so try this, it might/maybe work.

To follow-up on a requirement given by @wirrbel, the following struct is NOT variable length, but it does define and provide access to a variable length array of integers.

typedef struct  {
    size_t N;
    int *(elems[]);  // parens to ensure a pointer to an array
} A_t;

A_t *a = malloc //etc.

a->elems = malloc(sizeof(int) * N);

In this fashion several A_t structures can be included in a more general structure.

No, in general your two struct, A_t and A_5_t, are not interchangeable. The reason is that the version with the flexible array can have different padding in front of the elems field than versions with a fixed field length.

Whether or not your compiler implements a different padding or not, you can test by using the offsetof macro. But even if the offsets are the same for your particular compiler and platform, you'd better not rely on that if you want portable code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top