The following code fails to decode the latitude and longitude.
The output is: 132, 1, 1645780273, 77276230, 0, -0.000000, 0.000000
, doing a wrong conversion on the last 2 values latitude & longitude.
If I remove the first 4 values and delete uint32_t identifiers;
from the struct i get the correct result 132, 1, 1645780273, 77276230, 0, 59.877871, 10.465200
Any help in understanding this behavior ist very appreciated.
#include <stdio.h>
#include <string.h>
int main() {
union {
unsigned char bytes[36];
struct{
uint32_t identifier;
uint16_t telegram_size;
uint16_t telegram_version;
uint32_t seconds;
uint32_t nanoseconds;
uint32_t stat;
double latitude;
double longitude;
}unpack;
}telegram;
unsigned char values[] = {0x23, 0x4B, 0x4D, 0x42, 0x84, 0x00, 0x01, 0x00, 0x31, 0x9d, 0x18, 0x62, 0x46, 0x24, 0x9b, 0x04, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x3e, 0x2c, 0x11, 0x5e, 0xf0, 0x4d, 0x40, 0xCA, 0x32, 0xC4, 0xB1, 0x2E, 0xEE, 0x24, 0x40};
memcpy(telegram.bytes, values, 36);
for (int i=0;i<36;i++){
printf("%02X ", telegram.bytes[i]);
}
printf("n%u, %u, %u, %u, %u, %f, %fn", telegram.unpack.telegram_size, telegram.unpack.telegram_version, telegram.unpack.seconds, telegram.unpack.nanoseconds, telegram.unpack.stat, telegram.unpack.latitude, telegram.unpack.longitude);
return 0;
}
Update: As far as I understood from the comments below the struct looks like this:
2
Answers
This has to do with struct padding.
Generally speaking, members of a struct start at an offset which is a multiple of that member’s size for alignment purposes. Because of this, there are 4 padding bytes between the
stat
andlatitude
members so that the latter can reside at an offset that is a multiple of 8. That means the struct is 4 bytes larger than you think it is.If you’re able to, you should rearrange the members so that they align at a natural offset so that there is no padding between the members (although there may still be padding at the end).
If that’s not an option, you can pack the struct using a compiler-specific attribute. If you’re using gcc, you would do the following:
Note also that on some architectures, an unaligned read or write can cause a crash.
You are assuming that the structure is laid out with all members contiguous. That is not a safe assumption. Implementations are free to insert arbitrary padding into structure layouts between members and after the last member. In practice, they will do so in a manner that allows them to ensure that all members can be properly aligned in memory.
In your particular case, your 8-byte
doubles
want to be aligned on 8-byte boundaries. Your implementation is ensuring that they can be by putting them at offsets from the beginning of the structure that are multiples of 8 bytes. That involves inserting four bytes of otherwise unused padding betweenstat
andlatitude
, which your initialization does not account for. The overall structure will also have an 8-byte alignment requirement, and its total size will be a multiple of 8 bytes.Removing the four-byte initial member allows the two
double
s to be aligned without any padding, which is why that variation on your program worked as you expected.