When programming in C I have found it invaluable to pack structs using
GCCs __attribute__((__packed__))
[...]
Since you mention __attribute__((__packed__))
, I assume your intention is to eliminate all padding within a struct
(make each member have a 1-byte alignment).
Is there no standard for packing structs that works in all C
compilers?
... and the answer is "no". Padding and data alignment relative to a struct (and contiguous arrays of structs in stack or heap) exist for an important reason. On many machines, unaligned memory access can lead to a potentially significant performance penalty (though becoming less on some newer hardware). In some rare-case scenarios, misaligned memory access leads to a bus error that is unrecoverable (may even crash the entire operating system).
Since the C standard is focused on portability, it makes little sense to have a standard way to eliminate all padding in a structure and just allow arbitrary fields to be misaligned, since to do so would potentially risk making C code non-portable.
The safest and most portable way to output such data to an external source in a way that eliminates all padding is to serialize to/from byte streams instead of just trying to send over the raw memory contents of your structs
. That also prevents your program from suffering performance penalties outside of this serialization context, and will also allow you to freely add new fields to a struct
without throwing off and glitching the entire software. It'll also give you some room to tackle endianness and things like that if that ever becomes a concern.
There is one way to eliminate all padding without reaching for compiler-specific directives, though it's only applicable if the relative order between fields does not matter. Given something like this:
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
... we need the padding for aligned memory access relative to the address of the structure containing these fields, like so:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
... where .
indicates padding. Every x
must align to an 8-byte boundary for performance (and sometimes even correct behavior).
You can eliminate the padding in a portable way by using an SoA (structure of array) representation like so (let's assume we need 8 Foo
instances):
struct Foos
{
double x[8];
char y[8];
};
We've effectively demolished the structure. In this case, the memory representation becomes like this:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... and this:
01234567
yyyyyyyy
... no more padding overhead, and without involving misaligned memory access since we're no longer accessing these data fields as an offset of a structure address, but instead as an offset of a base address for what is effectively an array.
This also carries the bonus of being faster for sequential access as a result of both less data to consume (no more irrelevant padding in the mix to slow down the machine's relevant data consumption rate) and also a potential for the compiler to vectorize processing very trivially.
The downside is that it's a PITA to code. It's also potentially less efficient for random access with the larger stride in between fields, where often AoS or AoSoA reps will do better. But that's one standard way to eliminate padding and pack things as tightly as possible without screwing with the alignment of everything.