4

Why this is not allowed?

#include <cstdio>

struct Foo {
int fooId;
char arr[ ];
} fooList[] =
{ {1, {'a', 'b'}},
  {2, {'c', 'd'}}
};

int main()
{
  for (int i = 0; i < 2; i++)
    printf("%d %c\n", fooList[i].fooId, fooList[i].arr[0]);
}

whereas, this is allowed:

struct Foo {
int fooId;
char arr[2]; // this line modified
} fooList[] =
{ {1, {'a', 'b'}},
  {2, {'c', 'd'}}
};
robert
  • 33,242
  • 8
  • 53
  • 74
sud03r
  • 19,109
  • 16
  • 77
  • 96

3 Answers3

15

Only the last member of a C struct can be flexible as in arr[].

Shamelessly copying from paragraph 6.7.2.1, sub-paragraph 16 of the ISO C99 standard:

16 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. With two exceptions, the flexible array member is ignored. First, the size of the structure shall be equal to the offset of the last element of an otherwise identical structure that replaces the flexible array member with an array of unspecified length.106)...

EDIT:

As for C++, see this. Bottom-line: flexible array members are not allowed in C++ at all - at least for the time being.

Community
  • 1
  • 1
thkala
  • 84,049
  • 23
  • 157
  • 201
  • Thanks, the comment was before I could see your edited text ;) – sud03r Feb 01 '11 at 13:11
  • I think it's wrong in C++? In c you can malloc memory of any size and put there your struct, while in C++ new will always allocate sizeof(struct) memory. – UmmaGumma Feb 01 '11 at 13:15
  • @thkala: It is simply not allowed. You can hack around it in a similar way as C89 allows (which isn't surprising given that C++98 is based on C89), but this addition to C99 is just not valid C++ and hasn't been incorporated into C++0x. – Fred Nurk Feb 01 '11 at 13:16
  • @Fred: I assume you mean getting by with a `[1]` array and allocating additional memory. Thanks for the info. – thkala Feb 01 '11 at 13:19
  • Nice find on the linked question. – Fred Nurk Feb 01 '11 at 13:20
  • 1
    @Fred Nurk, it has not been and will never be incorporated into the language. The issue there is the same as with variable length arrays, they would break the type system. Currently the size of the array is part of the type, and is known at compile time, if that is replaced, then these particular types of arrays would not be usable for compile time constructs (consider `template int size_array( T (&)[ size ] ) { return size; }`. As always it could be stated in the standard, but that would create a second different "array" citizen. – David Rodríguez - dribeas Feb 01 '11 at 13:48
  • @DavidRodríguez-dribeas: Did I say otherwise? – Fred Nurk Feb 01 '11 at 13:50
  • @Fred Nurk, no, I just wanted to add to your answer (it is not allowed, will not be in the upcoming standard) that it is not that the issue was not discussed, but that a decision was taken not to do it. – David Rodríguez - dribeas Feb 01 '11 at 19:34
1

In C++ all members of an user defined type must have complete types, and the member arr does not have a complete type unless you give it a size.

In C, the struct definition would compile, but you might not get what you want. The problem is that an array without size is allowed at the end of a struct to be used as a proxy to access the contiguous block of memory after the instance. This allows a dumb vector implementation as:

typedef struct vector {
   int size;
   char buffer[];
} vector;
vector* create_vector( int size ) {
   vector* p = (vector*) malloc( sizeof *p + size ); // manually allocate "size" extra
   p->size = size;
};
int main() {
   vector* v = create_vector(10);
   for ( int i = 0; i < v->size; ++i )
      printf("%d\n", v->buffer[i] );
   free(v);
}

But the language does not allow you to initialize with the curly braces as the compiler does not know how much memory has to be held (in general, in some circumstances it can know). The size-less member of the struct is only a way of accessing beyond the end of the object, it does not hold memory in itself:

printf( "sizeof(vector)=%d\n", sizeof(vector) ); // == sizeof(int)
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • I would strongly rather not say it is used as a pointer, lest beginners get tripped up with arrays vs pointers again. Even though the member name ("buffer" above) is used to refer to the memory following the object, this isn't the same type of pointing that pointers do. – Fred Nurk Feb 01 '11 at 13:54
  • @Fred Nurk: I agree that it could cause confusion if read as *the array is a pointer*, so I have reworded, even if what really happens at this stage is exactly that: the array is used as a pointer (the only uses of that member are those that require the array to decay into a pointer) – David Rodríguez - dribeas Feb 01 '11 at 14:45
0

In C++03, this is not allowed in struct or class!

Comeau C++ compiler gives this error:

"ComeauTest.c", line 3: error:  
incomplete type is not allowed 
      char arr[ ];
           ^ 

Exactly simlar question yesterday : Difference between int* and int[] in C++

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851