tags:

views:

2306

answers:

5

I want to have an variable-length array contained within a structure, but am having trouble initializing it correctly.

struct Grid {
  int rows;
  int cols;
  int grid[];
}

int main() {
  struct Grid testgrid = {1, 3, {4, 5, 6}};
}

Everything I try gives me an 'error: non-static initialization of a flexible array member' error.

+5  A: 

You can make that work in gcc by making the struct either static or global, but it turns out that initializing flexible array members is non-conforming and so it is likely to not work except with gcc. Here is a way to do it that just uses C99-conforming features...

#include <stdlib.h>
#include <stdarg.h>

typedef struct Grid {
  int rows;
  int cols;
  int grid[];
} *Grid;

Grid newGrid(int, int, ...);

Grid newGrid(int rows, int cols, ...)
{
Grid g;
va_list ap;
int i, n = rows * cols;

  if((g = malloc(sizeof(struct Grid) + rows * cols * sizeof(int))) == NULL)
    return NULL;
  g->rows = rows;
  g->cols = cols;
  va_start(ap, cols);
  for(i = 0; i < n; ++i)
    g->grid[i] = va_arg(ap, int);
  va_end(ap);
  return g;
}
.
.
.
Grid g1, g2, g3;
g1 = newGrid(1, 1, 123);
g2 = newGrid(2, 3, 1, 1, 1,
                   2, 2, 2);
g3 = newGrid(4, 5, 1,  2,  3,  4,  5,
                   6,  7,  8,  9, 10,
                  11, 12, 13, 14, 15,
                  16, 17, 18, 19, 20);
DigitalRoss
I'll +1 if you separate that out a little bit so that it's slightly more readable. :P
Chris Lutz
I meant separating them into separate statements, but that's also a good idea.
Chris Lutz
I believe you are stuck with the limitation of `static` storage class, because there just isn't any way to get the compiler to make a variable length structure with `auto` storage class. There are variable length arrays in C99, so you could allocate space on the stack with a variable length array and then initialize a pointer to `struct Grid` from its address. It would probably even be a conforming program as long as the memory was exlusively accessed via the `struct *`. You could also `malloc` it. Then you could `memcpy` from the `static` struct to the `auto` one.
DigitalRoss
Sorry, but this is not C. I don't know what compiler you are using and what language extensions it supports, but what you wrote above is not C, neither C89/90 nor C99. In C aggregate initializers cannot be used to "create" flexible array members in structs, regardless of whether sthese structs are static or automatic.
AndreyT
@DigitalRoss: The statements in 6.7.8 apply only to freestanding arrays, but not to flexible array members (FAM) in structs. Technically, a FAM is not an "array" for the purposes of 6.7.8. Additionally, if the requirements of 6.7.8 applied to FAM, they would not be limited to static arrays only, would they?
AndreyT
GCC seems to allow the initialization of static flexible array members as an extension. The 'static' keyword is not necessary; you can initialize global variables with FAMs.
Jonathan Leffler
@Jonathan: I believe that this is an extension of GNU. I included a link to the GCC documents that describe it. If you use _paranoid mode_ (`-std=c99 -ansi -pedantic -Wall`), then it will emit warnings.
D.Shawley
A: 

A version using malloc:

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

typedef struct Grid {
  int rows;
  int cols;
  int *grid;
} Grid;

/* Should validate params */
Grid
buildGrid(int rows, int cols, int vec[]) {

    Grid grid;
    grid.rows = rows;
    grid.cols = cols;
    int i;

    if ( (grid.grid = malloc(sizeof(vec))) == NULL ) {
        /* do something.*/
    }

    for(i = 0; i < sizeof(vec) ; i++ ) {
        grid.grid[i] = vec[i];
    }

    return grid;
}
Macarse
`sizeof(vec)` won't work like you think it will. Arrays degrade to pointers when passes as a function, so that line will be the same as `sizeof(int *)` - not what you want.
Chris Lutz
Also, is there any reason not to go ahead and make it a _real_ two-dimensional array?
Chris Lutz
Chris: You are right. Thanks for pointing it out. A correct way would be passing and additional parameter sizeof(vec)/sizeof(vec[0]) as the size of the vec.
Macarse
A: 

I do not believe that this is possible or supported. As DigitalRoss points out, you can initialize from a literal in the case of static arrays... though I'm still not sure if this is included in the Standard or just a common extension. I can't seem to find a clause in the Standard that supports literal initialization of flexible arrays though I can see that gcc explicitly supports it.

D.Shawley
Comeau rejects it.
AndreyT
@Andrey - Comeau is a C++ compiler, not a C compiler, so it's expected to reject C99 code since the C99 standard isn't included in C++.
Chris Lutz
@ Chris Lutz: No. Comeau is a C++, C89/90 and C99 compiler, denending on how you invoke it.
AndreyT
Ah. I thought it was "Comeau C++" but it turns out to be "Comeau C/C++". I think I knew that at some point but forgot it.
Chris Lutz
+3  A: 

You don't have a variable length array (VLA) in your structure. What you have in your structure is called a flexible array member. Flexible array member has absolutely nothing to do with VLA. Flexible array members in C exist to legalize and support the good-old "struct hack" idiom, which is based on dynamic allocation of memory for struct objects with trailing arrays of different size.

Flexible array members cannot be initialized with aggregate initializers, which is what you seem to attempt in your code. What you are trying to do here is simply impossible. There's no such feature in C.

Meanwhile, the text of the error message generated by your compiler seems to suggest that it supports something like this as an extension. This might be true, but keep in mind that this is in no way a standard C feature.

AndreyT
Again, does not apply to flexible array members. Also note that GCC, one of the compilers that supports this extension, clearly and explicitly documents it as a GCC-specific extension.
AndreyT
Needless to say, invoking GCC in '-ansi -pedantic' mode results in a warning for an attempt to initialize a flexible array member.
AndreyT
I believe AudreyT is correct - elsewhere the standard says explicitly _"A structure type containing a flexible array member is an incomplete type that cannot be completed."_
caf
Sigh, if you had simply cited the standard we could have avoided the discussion. It turns out there is an example of flexible array initialization and the standard does say it is invalid. 6.7.2.1 (20)
DigitalRoss
Well, to be honest, I missed that example. Moreover, examples are usuanlly non-normative, so I was skipping them. I still can't find any explict wording in the normative text that would prohibit this.
AndreyT
@AndreyT: if you happen to find this in the Standard, please post the section and clause. I haven't found it in an hour or so of searching.
D.Shawley
...found it in 2 places. The first is **6.7.2.1** (16) where it says *with two exceptions* [it] *is ignored*, and since initialization is *not* one of the exceptions... i.e., the FAM is defined such that the rest of the standard doesn't apply to it! The second place is **6.7.2.1** (20), `struct s t3 = { 1, { 4.2 }}; // invalid: there is nothing for the 4.2 to initialize`. We should note that this is not technically an example (those are so labelled) but is in the same section as (16), and is subtitled "Semantics", so it's normative.
DigitalRoss
A: 
sambowry