Padding aligns structure members to "natural" address boundaries - say, int members would have offsets, which are mod(4) == 0 on 32-bit platform. Padding is on by default. It inserts the following "gaps" into your first structure:
struct mystruct_A {
char a;
char gap_0[3]; /* inserted by compiler: for alignment of b */
int b;
char c;
char gap_1[3]; /* -"-: for alignment of the whole struct in an array */
} x;
Packing, on the other hand prevents compiler from doing padding - this has to be explicitly requested - under GCC it's __attribute__((__packed__)), so the following:
struct __attribute__((__packed__)) mystruct_A {
char a;
int b;
char c;
};
would produce structure of size 6 on a 32-bit architecture.
A note though - unaligned memory access is slower on architectures that allow it (like x86 and amd64), and is explicitly prohibited on strict alignment architectures like SPARC.
Answer from Nikolai Fetissov on Stack OverflowPadding aligns structure members to "natural" address boundaries - say, int members would have offsets, which are mod(4) == 0 on 32-bit platform. Padding is on by default. It inserts the following "gaps" into your first structure:
struct mystruct_A {
char a;
char gap_0[3]; /* inserted by compiler: for alignment of b */
int b;
char c;
char gap_1[3]; /* -"-: for alignment of the whole struct in an array */
} x;
Packing, on the other hand prevents compiler from doing padding - this has to be explicitly requested - under GCC it's __attribute__((__packed__)), so the following:
struct __attribute__((__packed__)) mystruct_A {
char a;
int b;
char c;
};
would produce structure of size 6 on a 32-bit architecture.
A note though - unaligned memory access is slower on architectures that allow it (like x86 and amd64), and is explicitly prohibited on strict alignment architectures like SPARC.
(The above answers explained the reason quite clearly, but seems not totally clear about the size of padding, so, I will add an answer according to what I learned from The Lost Art of Structure Packing, it has evolved to not limit to C, but also applicable to Go, Rust.)
Memory align (for struct)
Rules:
- Before each individual member, there will be padding so that to make it start at an address that is divisible by its alignment requirement.
E.g., on many systems, anintshould start at an address divisible by 4 and ashortby 2. charandchar[]are special, could be any memory address, so they don't need padding before them.- For
struct, other than the alignment need for each individual member, the size of whole struct itself will be aligned to a size divisible by strictest alignment requirement of any of its members, by padding at end.
E.g., on many systems, if struct's largest member isintthen by divisible by 4, ifshortthen by 2.
Order of member:
- The order of member might affect actual size of struct, so take that in mind.
E.g., the
stu_candstu_dfrom example below have the same members, but in different order, and result in different size for the 2 structs.
Address in memory (for struct)
Empty space:
- Empty space between 2 structs could be used by non-struct variables that could fit in.
e.g intest_struct_address()below, the variablexresides between adjacent structgandh.
No matter whetherxis declared,h's address won't change,xjust reused the empty space thatgwasted.
Similar case fory.
Example
(for 64 bit system)
memory_align.c:
/**
* Memory align & padding - for struct.
* compile: gcc memory_align.c
* execute: ./a.out
*/
#include <stdio.h>
// size is 8, 4 + 1, then round to multiple of 4 (int's size),
struct stu_a {
int i;
char c;
};
// size is 16, 8 + 1, then round to multiple of 8 (long's size),
struct stu_b {
long l;
char c;
};
// size is 24, l need padding by 4 before it, then round to multiple of 8 (long's size),
struct stu_c {
int i;
long l;
char c;
};
// size is 16, 8 + 4 + 1, then round to multiple of 8 (long's size),
struct stu_d {
long l;
int i;
char c;
};
// size is 16, 8 + 4 + 1, then round to multiple of 8 (double's size),
struct stu_e {
double d;
int i;
char c;
};
// size is 24, d need align to 8, then round to multiple of 8 (double's size),
struct stu_f {
int i;
double d;
char c;
};
// size is 4,
struct stu_g {
int i;
};
// size is 8,
struct stu_h {
long l;
};
// test - padding within a single struct,
int test_struct_padding() {
printf("%s: %ld\n", "stu_a", sizeof(struct stu_a));
printf("%s: %ld\n", "stu_b", sizeof(struct stu_b));
printf("%s: %ld\n", "stu_c", sizeof(struct stu_c));
printf("%s: %ld\n", "stu_d", sizeof(struct stu_d));
printf("%s: %ld\n", "stu_e", sizeof(struct stu_e));
printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));
printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));
return 0;
}
// test - address of struct,
int test_struct_address() {
printf("%s: %ld\n", "stu_g", sizeof(struct stu_g));
printf("%s: %ld\n", "stu_h", sizeof(struct stu_h));
printf("%s: %ld\n", "stu_f", sizeof(struct stu_f));
struct stu_g g;
struct stu_h h;
struct stu_f f1;
struct stu_f f2;
int x = 1;
long y = 1;
printf("address of %s: %p\n", "g", &g);
printf("address of %s: %p\n", "h", &h);
printf("address of %s: %p\n", "f1", &f1);
printf("address of %s: %p\n", "f2", &f2);
printf("address of %s: %p\n", "x", &x);
printf("address of %s: %p\n", "y", &y);
// g is only 4 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
printf("space between %s and %s: %ld\n", "g", "h", (long)(&h) - (long)(&g));
// h is only 8 bytes itself, but distance to next struct is 16 bytes(on 64 bit system) or 8 bytes(on 32 bit system),
printf("space between %s and %s: %ld\n", "h", "f1", (long)(&f1) - (long)(&h));
// f1 is only 24 bytes itself, but distance to next struct is 32 bytes(on 64 bit system) or 24 bytes(on 32 bit system),
printf("space between %s and %s: %ld\n", "f1", "f2", (long)(&f2) - (long)(&f1));
// x is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between g & h,
printf("space between %s and %s: %ld\n", "x", "f2", (long)(&x) - (long)(&f2));
printf("space between %s and %s: %ld\n", "g", "x", (long)(&x) - (long)(&g));
// y is not a struct, and it reuse those empty space between struts, which exists due to padding, e.g between h & f1,
printf("space between %s and %s: %ld\n", "x", "y", (long)(&y) - (long)(&x));
printf("space between %s and %s: %ld\n", "h", "y", (long)(&y) - (long)(&h));
return 0;
}
int main(int argc, char * argv[]) {
test_struct_padding();
// test_struct_address();
return 0;
}
Execution result - test_struct_padding():
stu_a: 8
stu_b: 16
stu_c: 24
stu_d: 16
stu_e: 16
stu_f: 24
stu_g: 4
stu_h: 8
Execution result - test_struct_address():
stu_g: 4
stu_h: 8
stu_f: 24
address of g: 0x7fffd63a95d0 // struct variable - address dividable by 16,
address of h: 0x7fffd63a95e0 // struct variable - address dividable by 16,
address of f1: 0x7fffd63a95f0 // struct variable - address dividable by 16,
address of f2: 0x7fffd63a9610 // struct variable - address dividable by 16,
address of x: 0x7fffd63a95dc // non-struct variable - resides within the empty space between struct variable g & h.
address of y: 0x7fffd63a95e8 // non-struct variable - resides within the empty space between struct variable h & f1.
space between g and h: 16
space between h and f1: 16
space between f1 and f2: 32
space between x and f2: -52
space between g and x: 12
space between x and y: 12
space between h and y: 8
Thus address start for each variable is g:d0 x:dc h:e0 y:e8

data structures - What are the implications of a 'packed' keyword/feature? - Programming Language Design and Implementation Stack Exchange
Is a packed struct always the same in size?
The Lost Art of Structure Packing (2018)
What's the equivalent of C struct{...} __attribute__((packed))?
Videos
I am working on this C project and I have noticed that attribute((packed)) is used quite alot. I get that it helps optimise memory usage on the detriment of cpu performance. Is it a common practice to favor memory over performance in embedded projects? Or am I missing/misunderstanding something?
Padding and Packing
The layout of structures is defined by the ABI (Application Binary Interface). The typical mindset of an ABI designer is mechanical sympathy, with an eye towards performance (speed).
To talk about padding, we need to talk about alignment, size, and stride.
First is alignment. Native types tend to have an alignment, that is the architecture expects that a double is either 4 or 8 bytes aligned (depending). This alignment then bleeds into compound types: the only way to ensure that a double field is 8 bytes aligned is for the compound type containing the field to itself by 8 bytes aligned, hence a compound type's alignment is the greater alignment of any of its components.
Second is size. Native types have a size, too. For example a double is typically 8 bytes. The size of the compound type will depend on the size and alignment of its components, as per the ABI rules.
Finally is stride. The stride of a type is the spacing between consecutive array elements. Swift is one of the few languages differentiating size and stride, in C the stride is always the size.
With that in mind, here is a typical product type ABI, as can generally be found in C:
- The alignment of the product type is the maximum alignment of any of its fields.
- The first field starts at the first byte of its product type.
- Any subsequent field starts at the next offset which guarantees its alignment, which may lead to "in-between" padding bytes.
- The size of the struct is rounded-up to a multiple of its alignment, which may lead to "tail" padding bytes.
And since a picture is clearer than words:
struct ProductType {
int i;
char __padding_0[P0];
double d;
char __padding_1[P1];
char c;
char __padding_2[P2];
};
On x64, we have:
int: 4 bytes, 4-bytes aligned.double: 8 bytes, 8-bytes aligned.char: 1 byte, 1-byte aligned.
Therefore:
- P0 = 4. This is because
dmust start at an offset that is a multiple of 8, and afterithe offset is only 4. - P1 = 0. This is because
cmust start at an offset that is a multiple of 1, and any offset is. - P2 = 7. This is because the alignment of
ProductTypeis 8, hence its size must be a multiple of 8.
Note that Swift would have P2 = 0.
The effect of packing is, very simply, to remove all padding.1
1 I am aware that some compilers take the opportunity to also allow specifying the struct alignment when packing. That's a potentially useful extra feature, but it's not packing.
Implications of Packing
The first and foremost implication is that you should implement it properly, or users will curse you.
For example, it seems reasonable to allow taking a reference (or pointer) to a field of a packed struct. However, this reference (or pointer) may now be under-aligned: that is, its alignment may be strictly less than the expected alignment of a pointer of this type.
Different languages & toolchains handle the situation differently:
- In Zig, pointer alignment is a part of the pointer type, so a compilation error will follow if one attempts to use an under-aligned pointer where a regular pointer is expected.
- In GCC (C or C++), the compiler handles under-aligned pointers properly within the function in which the pointer was created, but allows passing the under-aligned pointers to functions expecting a regular pointer... which leads to Undefined Behavior.
- In Rust, manipulating packed fields is
unsafe, and the user is on the hook to tread carefully, or they will trigger Undefined Behavior.
I would argue that the situation of being able to create an under-aligned pointer, but then have to walk on eggshells forever, is not desirable from a user point of view, and thus that language designers going the packed way should either disallow forming such pointers, or follow through and ensure they provide users the tooling they need to manipulate under-aligned pointers.
Alternatives
There are alternatives to packing, for the memory conscious.
As mentioned, packing is about avoiding padding, and there are two sources of padding:
- In-between fields padding.
- Tail padding.
Tail padding is eliminated in a language such as Swift which differentiates size & stride.
In-between fields padding is eliminated in a language such as Rust which reserves the right to re-order fields arbitrarily -- and typically will reorder them by descending alignment, which removes all in-between fields padding.
Those two alternatives, combined, remove nearly2 any and all padding in a struct3 without introducing under-aligned references and pointers into the fray.
I would typically advise considering them first:
- Provide the same memory gains as packed, without taxing the user.
- Without performance penalty incurred from under-aligned pointers.
- Without headaches induced by under-aligned pointers.
You may still want to later introduce under-aligned pointers in the language, separately.
2 Unless reordering can interleave the fields from different field structs, there may still be some padding in between structs. For example, a struct A (int, char) followed by itself would still have 3 bytes of padding between the two instances, no matter the order.
3 Padding may still occur between array elements, the exact amount of padding inserted being equal to stride - size.
According to “The Lost Art of Structure Packing” by Eric S. Raymond, structure packing can slow down your program or worse:
The first thing to understand is that, on modern processors, the way your compiler lays out basic datatypes in memory is constrained in order to make memory accesses faster. Our examples are in C, but any compiled language generates code under the same constraints.
…
Self-alignment [that is, aligning to an address divisible by the size of the datatype] makes access faster because it facilitates generating single-instruction fetches and puts of the typed data. Without alignment constraints, on the other hand, the code might end up having to do two or more accesses spanning machine-word boundaries.
…
I said “on modern processors” because on some older ones forcing your C program to violate alignment rules (say, by casting an odd address into an int pointer and trying to use it) didn’t just slow your code down, it caused an illegal instruction fault.
On the other hand, structure packing is useful “if you intend to write code for memory-constrained embedded systems, or operating-system kernels. It is useful if you are working with application data sets so large that your programs routinely hit memory limits. It is good to know in any application where you really, really care about optimizing your use of memory bandwidth and minimizing cache-line misses.” Raymond goes on to describe a program of his that kept getting OOM errors, for which he managed “to cut the working-set size by around 40%” by carefully arranging his structures.
Can a struct with packed attribute be always the same in size?
How can I be sure that every element will be the same?
I want to avoid parsing a buffer and just cast the buffer and assign it into a pointer. But I have to be sure that the struct will remain the same in size across compilations
PS: I mean the same without removing or adding members
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.
In a struct, what matters is the offset of each member from the address of each struct instance. Not so much is the matter of how tightly things are packed.
An array, however, matters in how it is "packed". The rule in C is that each array element is exactly N bytes from the previous, where N is the number of bytes used to store that type.
But with a struct, there is no such need for uniformity.
Here's one example of a weird packing scheme:
Freescale (who make automotive microcontrollers) make a micro that has a Time Processing Unit co-processor (google for eTPU or TPU). It has two native data sizes, 8 bits and 24 bits, and only deals with integers.
This struct:
struct a
{
U24 elementA;
U24 elementB;
};
will see each U24 stored its own 32 bit block, but only in the highest address area.
This:
struct b
{
U24 elementA;
U24 elementB;
U8 elementC;
};
will have two U24s stored in adjacent 32 bit blocks, and the U8 will be stored in the "hole" in front of the first U24, elementA.
But you can tell the compiler to pack everything into its own 32 bit block, if you want; it's more expensive on RAM but uses less instructions for accesses.
"packing" doesn't mean "pack tightly" - it just means some scheme for arranging elements of a struct wrt the offset.
There is no generic scheme, it is compiler+architecture dependent.