Considering the mentioned platforms, yes, packed structs are completely fine to use. x86 and x86_64 always supported unaligned access, and contrary to the common belief, unaligned access on these platforms has (almost) the same speed as aligned access for a long time (there's no such thing that unaligned access is much slower). The only drawback is that the access may not be atomic, but I don't think it matters in this case. And there is an agreement between compilers, packed structs will use the same layout.
GCC/clang supports packed structs with the syntax you mentioned. MSVC has #pragma pack
, which can be used like this:
#pragma pack(push, 1)
struct Sensor1Telemetry {
int16_t temperature;
uint32_t timestamp;
uint16_t voltageMv;
// etc...
};
#pragma pack(pop)
Two issues can arise:
- Endianness must be the same across platforms (your MCU must be using little-endian)
- If you assign a pointer to a packed struct member, and you're on an architecture which doesn't support unaligned access (or use instructions which have alignment requirements, like
movaps
or ldrd
), then you may get a crash using that pointer (gcc doesn't warn you about this, but clang does).
Here's the doc from GCC:
The packed attribute specifies that a variable or structure field
should have the smallest possible alignment—one byte for a variable
So GCC guarantees that no padding will be used.
MSVC:
To pack a class is to place its members directly after each other in
memory
So MSVC guarantees that no padding will be used.
The only "dangerous" area I've found, is the usage of bitfields. Then the layout may differ between GCC and MSVC. But, there's an option in GCC, which makes them compatible: -mms-bitfields
Tip: even, if this solution works now, and it is highly unlikely that it will stop working, I recommend you keep dependency of your code on this solution low.
Note: I've considered only GCC, clang and MSVC in this answer. There are compilers maybe, for which these things are not true.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…