23

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.

  • 1
    I edited the question above to illustrate... –  Oct 13 '09 at 03:16
  • 1
    Which compiler, on which platform? – Jonathan Leffler Oct 13 '09 at 04:14
  • So, you want to be able to create two different GRid objects with different length grid[] arrays. What number should `sizeof Grid` give you? If I want to make an array of Grid objects, how many bytes should the compiler allocate for each element? You need to make grid a pointer to int. – PaulMurrayCbr May 13 '16 at 04:11

5 Answers5

27

Here is my version:

#include <stdio.h> 

struct matrix {
  int rows;
  int cols;
  int **val;
} a = {        .rows=3,  .cols=1,
        .val = (int*[3]){ (int[1]){1},
                          (int[1]){2},
                          (int[1]){3} } },

  b = {        .rows=3,  .cols=4,
        .val = (int*[3]){ (int[4]){1, 2, 3, 4},
                          (int[4]){5, 6, 7, 8},
                          (int[4]){9,10,11,12} } };

void print_matrix( char *name, struct matrix *m ){
  for( int row=0;row<m->rows;row++ )
    for( int col=0;col<m->cols;col++ )
      printf( "%s[%i][%i]: %i\n", name, row, col, m->val[row][col] );
  puts("");
}

int main(){
  print_matrix( "a", &a );
  print_matrix( "b", &b );
}
sambowry
  • 2,436
  • 1
  • 16
  • 13
  • 5
    I know this is an old thread, but that just solved a big problem that I was having. The useful bit is that the arrays can be different sizes. I'm surprised that I couldn't find the answer anywhere else. Thank you. – kainosnous Nov 12 '10 at 10:27
  • 1
    Also quite useful for me! Other answers seem not to address the essential requirement of the question. Many thanks! – basicthinker Jul 19 '13 at 08:24
  • Thank you. I've finally found that ad hoc solution I've been looking now and then for months. – mesmerizingr Jun 17 '14 at 14:15
  • 1
    The most useful aspect of this answer is the idea of creating a *pointer* in the struct, but initializing it with an *array* compound literal. Without the `(int*[])` cast, the compiler will complain. – nitrogen Jul 06 '14 at 19:32
  • This doesn't appear to be portable. Gcc happily accepts the following: `typedef struct node { int **data; } node; node n = { (int*[2]) { (int[1]) {5}, (int[1]) {1} } };` But cl.exe (visual studio 2013) is another story. When the code is in a .c file, cl.exe compiles it just fine. But when it's in a .cpp file, cl.exe barfs with this: `thing.cpp(15) : error C2059: syntax error : '{' thing.cpp(15) : error C2143: syntax error : missing ';' before '{' thing.cpp(15) : error C2447: '{' : missing function header (old-style formal list?) ...` – Ewat Jul 14 '16 at 22:56
12

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
  • 143,651
  • 25
  • 248
  • 329
  • 2
    I'll +1 if you separate that out a little bit so that it's slightly more readable. :P – Chris Lutz Oct 13 '09 at 03:13
  • I meant separating them into separate statements, but that's also a good idea. – Chris Lutz Oct 13 '09 at 03:19
  • 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 Oct 13 '09 at 03:42
  • 1
    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. – AnT stands with Russia Oct 13 '09 at 03:44
  • 1
    @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? – AnT stands with Russia Oct 13 '09 at 04:02
  • 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 Oct 13 '09 at 04:15
  • @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 Oct 13 '09 at 12:24
4

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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 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. – AnT stands with Russia Oct 13 '09 at 04:13
  • Needless to say, invoking GCC in '-ansi -pedantic' mode results in a warning for an attempt to initialize a flexible array member. – AnT stands with Russia Oct 13 '09 at 04:20
  • 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 Oct 13 '09 at 04:25
  • 1
    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 Oct 13 '09 at 04:26
  • 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. – AnT stands with Russia Oct 13 '09 at 04:38
  • @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 Oct 13 '09 at 12:27
  • ...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 Oct 13 '09 at 21:46
1

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.

Community
  • 1
  • 1
D.Shawley
  • 58,213
  • 10
  • 98
  • 113
-1

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
  • 91,829
  • 44
  • 175
  • 230
  • 3
    `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 Oct 13 '09 at 03:41
  • Also, is there any reason not to go ahead and make it a _real_ two-dimensional array? – Chris Lutz Oct 13 '09 at 03:43
  • 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 Oct 13 '09 at 12:48
  • Since you changed `int[] grid` to `int *grid` you can as well use a static initializer in C99 and forget all the nonsense above: `struct Grid testgrid = {1, 3, (int[3]){4, 5, 6}};` (sorry for reviving but this trivial solution should be mentioned ...) – Simon Urbanek Feb 06 '12 at 15:17