I came across the following c code.
union packet_t {
uint8_t raw[10];
struct {
union {
uint16_t number;
uint8_t number_byte[2];
};
union {
uint32_t size;
uint16_t size_word[2];
uint8_t size_byte[4];
};
uint8_t body[4];
};
} packet;
When I try to test it, I came across some weird behavior, I hope someone can help me figure out what's wrong with this definition. I did some search, but there were no similar problems.
Here is what I tried:
uint8_t test1[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a};
int i = 0;
for(i = 0; i < sizeof(test1); i++)
{
packet.raw[i] = test1[i];
}
printf("packet.size_word[0]=0x%04x\n", packet.size_word[0]);
printf("packet.size_word[1]=0x%04x\n", packet.size_word[1]);
Output is
packet.size_word[0]=0x0605
packet.size_word[1]=0x0807
It completely missed 0x03 and 0x04.
When I use the following definition (remove "uint32_t size;"), it works fine.
union packet_t {
uint8_t raw[10];
struct {
union {
uint16_t number;
uint8_t number_byte[2];
};
union {
uint16_t size_word[2];
uint8_t size_byte[4];
};
uint8_t body[4];
};
} packet;
Here is the output:
packet.size_word[0]=0x0403
packet.size_word[1]=0x0605
Does anyone know why this is happening? I thought items in union always occupied same memory location.
Here is the link for the code.
--------------------------Update 11/25/2018---------------------------------
So I'm confident now it's the structure padding and it depends on the CPU architecture.
I tested on Arduino with Atmega328p (an 8-bit MCU), it works like a charm. There was no struct padding since the MCU process 1 byte each time.
However, the code is not portable at all as @selbie mentioned, when writing such code, we have to consider CPU architecture and environment. Simplest solution is not using it.
Further reading: