I want to create a library for a protocol. Within a packet of this protocol, there is a field which can be up to 7 32-Bit numbers. These are used as indicators for the payload, so if I were to only need 3 of them, I would only have 3 32-bit numbers. The problem here is, that the rest of the packet needs to "move up", so that the data in the packet is continuous and without any padding. My question here would be, how to best implement this behavior.
I cant see fixed size arrays like this working:
struct packet{
uint32_t header;
uint32_t indicator_fields[7];
....(payload and trailer)
}
My understanding is, that if I would only add 3 indicators, it would still have 4 *32 bit buffer. I cant use dynamic sized arrays, as I can't use the heap in this project. The only thing that would work is defining 7 structs each with n indicators, but I'd like to avoid that.
You can't have VLA inside a struct (or at file scope). You can have flexible array members at the end of a struct, but these were mainly designed with dynamic allocation in mind. You can kind of still use flexible array members for statically allocated memory, but it is hacky and you risk end up with poorly-defined behavior.
What I would recommend instead in this case is to use a raw buffer of uint32_t
, set to the maximum size you need to support. Then if you for some reason needs to present that in various different ways, you can use a union
to wrap all possible use-cases for the protocol:
typedef union
{
uint32_t raw [MAX_ITEMS];
packet_this_t this_type;
packet_that_t that_type;
...
} protocol_t;
This relies heavily on not having any struct members that cause padding or misalignment.
But it is nice since you can type pun between the various union members pretty wildly without invoking poorly-defined behavior (such as "strict aliasing violations"). If you for example fill up some raw DMA buffer or similar consisting of a uint32_t
array, you could cast a pointer from that into a protocol_t*
and then access individual struct members.