I have looked around this site to try to figure out if my use of casting to different unions is violating strict aliasing or otherwise UB.
I have packets coming in on a serial line and I store/get them like:
union uart_data {
struct {
uint8_t start;
uint8_t addr;
uin16_t length;
uint8_t data[];
};
uint8_t bytes[BUFFER_SIZE];
};
void store_byte(uint8_t byte) {
uart_data->start = byte;
/* and so on with the other named fields. */
}
uint8_t * get_buffer() {
return uart_data->bytes;
}
My understanding is that this is, at least with GCC and GNU extensions an valid way to do type punning.
However, I then want to cast the return value from get_buffer()
to a more specific type of packet that the uart doesn't need to know the details about.
union spec_pkt {
struct {
uint8_t start;
uint8_t addr;
uin16_t length;
uint8_t command;
uint8_t some_field;
uint16_t data_length;
uint8_t data[];
};
uint8_t bytes[BUFFER_SIZE];
};
void process(uint8_t *data) {
union specific_pkt *pkt = (union specific_pkt *)data;
}
I recall having read somewhere that this is valid since I'm casting from a type that exists in the union but I can't find the source.
My rationale for doing this it this way is that I can have a uart driver that only needs to know about the lowest level details. I'm on an MCU so I only have access to pre-allocated buffers to data and this way I don't have to memcpy
between buffers, wasting space. And in my application code I can handle the packet in a nicer way than:
uint8_t data[BUFFER_SIZE];
data[START_POS];
data[LEN_POS];
data[DATA_POS];
If this is violating the SA rule or is UB I'd love some alternatives to achieve the same.
I'm using GCC on a target that supports unaligned access and GCC allows type punning through unions.