Home > database >  How to get around zero-sized arrays?
How to get around zero-sized arrays?

Time:11-11

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?

CodePudding user response:

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.

CodePudding user response:

Before C99, there's no standard-compliant way. In C99,

typedef struct {
    uint16_t id;
    uint16_t len;
    uint8_t data[];
} packet_t;

is the legal way to do a flexible length struct.

Your union was nearly useless synthetic sugar, anyways: you needed to know the type externally anyways, and C is not type-restrictive, so whether you write packet.data16[2] or uint16_t* data = packet.data; data[2] makes jolly little difference.

There's no standards-compliant way to keep an array of union type with different member lengths as the last element.

CodePudding user response:

If you use a pre-C99 compiler then a simple common trick is to set the size to 1. Of course you also need to add/subtract the size by 1 when handling it

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

See also

  • Related