Search code examples
arraysclanguage-lawyerflexible-array-member

How to get around zero-sized arrays?


In a library I'm working with I found a structure like this:

typedef struct {
    uint16_t id;
    uint16_t len;
    union {
        uint8_t data[0];
        uint16_t data16[0];
        uint32_t data32[0];
    }
} packet_t;

So this is a packet with flexible-lengthed data, with an access to each of the word lengths.

But the standard says an object cannot have zero size, so compiling this raises a warning ("ISO C forbids zero-size array").

I compile the code in gcc so this zero-sized array hack will just do fine thanks to its extension. But is there any way I can be really "pedantic" about this? Flexible Array Member didn't help because of the union.

It will be simple if I just throw away the multibyte data members, but I want to keep them as much as possible because some functions in the library rely on these.

At first I thought I could use one-sized arrays instead, but I'm not sure if there is no side affects at all. Any recommendations?


Solution

  • It's not impossible to produce something that compiles with the -pedantic flag. But I don't think you can get the exact same semantics syntax-wise without C11.

    The transformed packet_t may look like this

    typedef union packet_t {
        struct { uint16_t id;   uint16_t len; };
        struct { uint16_t id8;  uint16_t len8;  uint8_t data[];    };
        struct { uint16_t id16; uint16_t len16; uint16_t data16[]; };
        struct { uint16_t id32; uint16_t len32; uint32_t data32[]; };
    } packet_t;
    

    The anonymous struct (C11) is required to nestle the fields as members of packet_t. It's what makes p->id valid still. The other id* and len* fields are dummies, because a flexible array member may not be the only member of a structure. They are there to make the access well-formed according to the common initial sequence guarantee of unions.

    Looking at it, I can't help but feel you may be better off ditching the multiple data fields altogether. A uint8_t flexible array member can contain whatever the other two may contain.