1

Please consider the following two structures:

typedef struct { 
    int num_data;
    char * name_data;
    int data[]; 
} part_t; 
typedef struct { 
    int num_parts;
    char * name_parts;
    part_t parts[]; 
} container_t; 

Ideally, I would be able to initialize a container something like this:

const container_t container = { 
    2,
    "Name of first container", 
    { 
            { 4, "Name of first part", { 1, 2, 3, 4 } }, 
            { 5, "Name of first part", { 1, 2, 3, 4, 5 } } 
    } 
};

My compiler says: "error: too many initializers"

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
lupin3
  • 11
  • 2
  • That's not even how a simple variable-length struct works... Try a simpler example first. – Kerrek SB Dec 04 '12 at 23:26
  • 4
    That's a 'flexible array member' rather than a VLA. – Jonathan Leffler Dec 04 '12 at 23:27
  • 2
    In short: You can't initialise flexible array members, and having an array of `struct`s with a flexible array member is an indexing catastrophe waiting to happen. – Daniel Fischer Dec 04 '12 at 23:44
  • @DanielFischer: Conceptually, it's no worse than `int foo[] = {1,2,3,4};`, and I've definitely encountered places where such a thing would have been useful if permitted. I wouldn't be surprised if some compilers allow such a construct, since it's clear what the semantics "should be" on any compiler that can handle it [allocate the proper amount of space in the initialized data area, have the struct name represent an lvalue at at address, and have sizeof(structname) equal the allocated size]. Although the feature would be useful, I don't know if its useful enough to justify implementing it. – supercat Dec 05 '12 at 16:29
  • @supercat You're only talking about initialising a flexible array member, I suppose? Yes, that could be useful sometimes, but that would mean the type depends on the length of the flexible array member, you'd have `struct foo` then (borrowing a bit of C++ template notation). If the implementation supports VLAs, I suppose it would be possible to implement. But it would be a nontrivial change in the language. – Daniel Fischer Dec 05 '12 at 16:53
  • @DanielFischer: My point was that such a feature would hardly be nonsensical, and no more of a "disaster waiting to happen" than some other features that already exist. I agree that the effort required to implement it would in many cases would be sufficiently high as to suggest that the standard shouldn't require it, but would regard it as a reasonable extension on compilers whose design would allow for relatively easy implementation. – supercat Dec 05 '12 at 17:13
  • @supercat If an array of structs with flexible array members were allowed, how would you index it? If flexible array members made a family of length-paremeterised types, `struct foo ar[12];` would be okay, just like arrays of VLAs. But with only one type `struct foo` regardless of the length you give the flexible array member, what would prevent you stuffing `struct foo`s with different array lengths into the array? – Daniel Fischer Dec 05 '12 at 17:19
  • @DanielFischer: Variable-length arrays cannot be used as array elements; that would not change even one could compile-time initialize and auto-size structs with VLAs. As for determining the size, one would either have the array data end with a sentinel value, or use `sizeof()` to determine the size of the array, i.e. one would use the same techniques one would employ when using an initialized array of auto-determined length. – supercat Dec 05 '12 at 18:15
  • @supercat `int dim; scanf("%d", &dim); if (dim < 1) exit(EXIT_FAILURE); double arr[15][dim];` no problem, an array of 15 VLAs of type `double[dim]`. I have the feeling we're talking past each other. `struct foo { int bar; int baz[]; }; struct foo f1 = { 1, { 2,3,4 } }, f2 = { 2, { 5,6 } }; struct foo arr[12]; arr[0] = f1; arr[1] = f2;` later `struct foo member = arr[i];`, how should that work? – Daniel Fischer Dec 05 '12 at 18:30
  • @DanielFischer: The declaration of `arr` would fail. One could say `struct foo { int n; int baz[]; }; struct foo f1 = { 3, { 2,3,4 } }, f2 = { 2, { 5,6 } }; struct foo *arr[12]; arr[0]=&f1; arr[1] = &f2;`. The person typing the `n` value for each instance would be responsible for ensuring it matched the size of the array. – supercat Dec 11 '13 at 20:22
  • @supercat What you have there is an array of pointers to `struct foo`, that's entirely unproblematic. What would be difficult at best would be an array of `struct foo`, since with the flexible array member, you don't know how many bytes each array element occupies. – Daniel Fischer Dec 11 '13 at 20:39
  • @DanielFischer: Structure types with flexible-array members cannot appear in arrays, whether they are initialized or not. I don't see that I ever said arrays of structs containing FLAs should be legal--merely that initialization of structs containing FLAs should be able to allocate as much space as required for the FLA. – supercat Dec 11 '13 at 20:49
  • @supercat I know. The language explicitly forbids that. I thought the entire shemozzle was "My point was that such a feature would hardly be nonsensical, and no more of a "disaster waiting to happen" than some other features that already exist" that you didn't believe allowing that would be "an indexing catastrophe waiting to happen", as I wrote in my first comment, referring to the OP's `part_t parts[];` array of structs with a flexible array member in `container_t`. – Daniel Fischer Dec 11 '13 at 20:56
  • @DanielFischer: I was meaning to imply that initialization of FLA members is conceptually no worse than `int foo[] = {1,2,3,4};`. We agree with regard to arrays of FLA-containing structures. – supercat Dec 11 '13 at 22:31
  • @supercat So much ado about nothing, and we misunderstood each other a lot. Well, that happens. There's a small difference between initialisation of flexible array members and an array declaration with an incomplete type in so far as a struct with a flexible array member is a complete type, while the array type is only complete with the dimension, and its type is determined by the initialiser. So with the array, the type gives you (well, not you, the implementation) the size, which for structs with flexible array members it wouldn't. Not an unsurmountable problem. – Daniel Fischer Dec 11 '13 at 22:39

1 Answers1

4

ISO/IEC 9899:2011 §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.

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.

The trouble is that you cannot embed a structure with a flexible array member into an array or another structure. You can have pointers to such structures, but not instances of such structures.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Interestingly, CERT says that a struct with a flexible array member *can* be used as the last member of another struct; but they don't cite the standard in that point. Mentioned in point 3 of the numbered list: https://www.securecoding.cert.org/confluence/display/c/DCL38-C.+Use+the+correct+syntax+when+declaring+a+flexible+array+member – hmijail Oct 16 '16 at 23:37
  • 1
    It's under discussion in the C standards body. See N2083 from the [PrePittsburgh2016](http://www.open-std.org/jtc1/sc22/wg14/) mailing at the WG14 web site. As yet, it is not legitimate; CERT is jumping the gun, at best. – Jonathan Leffler Oct 16 '16 at 23:39
  • Thank you for that pointer. I commented to the CERT document. – hmijail Oct 17 '16 at 00:49
  • This is picking a nit, but I think it is permitted to embed a struct with a flexible array member into a union. It's not explicitly prohibited anywhere I've seen, and the quoted paragraph 3 implies that it is permitted when it states, "any union containing, possibly recursively, a member that is such a structure". – ov2k Sep 01 '19 at 17:59
  • Nits deserve to be picked, @ov2k, and this is no exception. I think you're probably right; I've revised the text accordingly (and fixed an appearance of "an an" too). – Jonathan Leffler Sep 01 '19 at 19:48