1

I have a stuct a of size 16 bytes and another struct b which contains a. Why is struct b of size 40 bytes? Where is the additional padding exactly?

typedef struct {
  
} a;

typedef struct {
  
  a x;
  
} b;
sam
  • 59
  • 1
  • 2
  • 7
  • 2
    You might want to learn about *alignment*. Each type will have a specific alignment, for example `double` typically ends up on addresses that are multiples of `8`, and `float` on addresses that are multiple of `4`. So for the `a` structure, there will likely be three bytes of padding between the `w` and `x` members. You can use the [`offsetof`](https://en.cppreference.com/w/c/types/offsetof) macro to check the exact offsets of each member. – Some programmer dude Apr 04 '22 at 13:33
  • 1
    Check the alignment via `offsetof()`. – the busybee Apr 04 '22 at 13:33
  • 2
    With that said, alignment and padding are implementation details that may differ from system to system, and even from compiler to compiler. And in a vast majority of cases doesn't really matter. So why does it matter for you? If it's plain curiosity then that's fine and okay, but please state so in the question itself. Otherwise if you have another problem because of alignment or padding, then please ask about your actual and underlying problem directly instead. – Some programmer dude Apr 04 '22 at 13:36
  • Note: ordering members by `sizeof` biggest to smallest usually results in smaller in memory footprints of `struct`s due to minimizing padding. – Mgetz Apr 04 '22 at 13:59
  • 1
    sam, Why is the size important? Code can certainly use `sizeof(b)` rathe than `40`. Knowing _why_ improves the value of the question. – chux - Reinstate Monica Apr 04 '22 at 14:31
  • This still feels very much like an [XY problem](https://mywiki.wooledge.org/XyProblem). – Some programmer dude Apr 05 '22 at 05:21

3 Answers3

0

The struct a has 4-byte alignment since that's the largest alignment of any of its members (i.e. float).

From there, the fields of b are laid out with the following offsets:

  • w: 0
  • padding: 1 - 3
  • x: 4 - 19
  • padding: 20 - 23
  • y: 24 - 31
  • z: 32 - 33
  • padding: 34 - 39

The member with the largest alignment is y which has 8-byte alignment. This results in 4 bytes of padding between x and y, as well as 6 bytes of padding at the end.

The padding can be minimized by moving z to between w and x. Then you would have:

  • w: 0
  • padding: 1
  • z: 2 - 4
  • x: 4 - 19
  • padding: 20 - 23
  • y: 24 - 31

For a total size of 32.

Alignment is of course entirely up to the implementation, but this is what you'll most likely see. The Lost Art of C Structure Packing goes into this in much more detail.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • 1
    The alignment requirement of a structure is the strictest alignment requirement of any of its members (or greater, if a C implementation chooses an unnecessarily restrictive alignment for some reason). It is not determined by the alignment of the largest member. – Eric Postpischil Apr 04 '22 at 13:43
  • Thanks, ok I understand the padding 34-29 since struct b has to end on a multiple of 8. But why is there a padding 20-23? x is ending on a multiple of 4. – sam Apr 04 '22 at 13:55
  • @shardy `y` needs to start at at offset that is a multiple of 8, so that means 4 bytes of padded before it are needed. – dbush Apr 04 '22 at 13:57
0

You seem to understand why the size of a is 16 bytes, and I'll also assume that you see why a has an alignment requirement of 4 bytes (see the note about arrays in the discussion below on the alignment of b). From that, we can see that, in b, we will have 3 bytes of padding between w and x.

Now, on your platform (as on many/most), a double has a size and alignment requirement of 8 bytes – so, after the 20 bytes up to the end of x (1 for w, 3 for padding and 16 for x), we need to add 4 bytes padding between x and y, to align that double. Thus, we have now added 12 bytes to our total, giving a running size (up to y) of 32 bytes.

The z member takes another 2 bytes (running size of 34) but, as the alignment requirement of the overall b structure must be 8 bytes (so that, in an array of such, every element's y will remain properly aligned), we need a further 6 bytes to reach a size (40) that is a multiple of 8.

Here is a breakdown of the memory layout:

typedef struct {
    char w;     //  1 byte:  total =  1
         // 3 bytes padding: total =  4
    float x;    //  4 bytes: total =  8
    short int y;//  2 bytes: total = 10
         // 2 bytes padding: total = 12
    float z;    //  4 bytes: TOTAL = 16
} a;

typedef struct {
    char w;     //  1 byte:  total =  1
         // 3 bytes padding: total =  4
    a x;        // 16 bytes: total = 20
         // 4 bytes padding: total = 24
    double y;   //  8 bytes: total = 32
    short int z;//  2 bytes: total = 34
         // 6 bytes padding: TOTAL = 40
} b;
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • [A quick and dirty (and non-portable) demo showing minimal padding on X86 and ARM64](https://godbolt.org/z/qxrEEsYjM) – Mgetz Apr 04 '22 at 14:10
  • "on your platform (as on many/most), a double has a size and alignment requirement of 8 bytes" --> I doubt the _most_ part. These days, most platforms are embedded ones (billions/year) and likely use smaller alignment needs. – chux - Reinstate Monica Apr 04 '22 at 14:29
  • @chux The alignment requirement(s) enforced by a compiler do not necessarily reflect those of the target architecture. IIRC, x86-64 doesn't require 8-byte alignment for `double`, but MSVC still gives 8 for `Alignof(double)`. Same for `int` (gives 4). But I could very well be wrong about the x86-64 hardware requirements. – Adrian Mole Apr 04 '22 at 14:31
  • @chux-ReinstateMonica that's why I was very very hesitant to say optimal with my demo. Because there are a lot of considerations there when padding may even be ideal. – Mgetz Apr 04 '22 at 14:32
  • @AdrianMole IIRC MSVC does that to avoid a `double` straddling a cache line which should be impossible at 8 byte alignment – Mgetz Apr 04 '22 at 14:33
  • @Mgetz I also believe that aligning to the size (with `double` or even `int`) certainly *optimizes* memory access. But maybe I'm just repeating what you already said. – Adrian Mole Apr 04 '22 at 14:54
  • @AdrianMole AFAIK x86 operates on 8 byte chunks on DDR4 memory controllers and 4byte x2 chunks on DDR5. The memory controller deals with alignment beyond that. So alignment isn't strictly enforced for any type. But that doesn't mean there aren't fun issues with cache lines etc. – Mgetz Apr 04 '22 at 14:57
0

The algorithm compilers typically use to lay out structures is described in this answer.

For your structure a and characteristics typical in current C implementations (described below):

  • The char w has an alignment requirement of 1 byte, so it needs no padding and is placed at offset 1. It occupies 1 byte.
  • The float x has an alignment requirement of 4 bytes, so 3 bytes are needed to bring the current offset from 1 byte to 4 bytes. Then it occupies 4 bytes, giving us 8 total so far.
  • The short int y has an alignment requirement of 2 bytes, so it needs no padding from the current offset of 8 bytes. It occupies 2 bytes, bringing the total to 10.
  • The float z has an alignment requirement of 4 bytes, so it needs 2 bytes to bring the offset from 10 bytes to 12 bytes. It occupies 4 bytes, bringing the total to 16.
  • The alignment requirement of the structure is 4 bytes (the strictest alignment requirement of its members), and the current total is 16 bytes, which is already a multiple of 4, so no padding at the end is needed.

Thus the size of a is 16 bytes.

For your structure b:

  • The char w has an alignment requirement of 1 byte, so it needs no padding and is placed at offset 1. It occupies 1 byte.
  • The a x has an alignment requirement of 4 bytes, so 3 bytes of padding are needed to bring the offset from 1 byte to 4 bytes. It occupies 16 bytes, bringing the total to 20.
  • The double y has an alignment requirement of 8 bytes, so 4 bytes of padding are needed to bring the offset from 20 bytes to 24. It occupies 8 bytes, bringing the total to 32.
  • The short int z has an alignment requirement of 2 bytes, so no padding is needed from the current offset of 32 bytes. It occupies 2 bytes, bringing the total to 34.
  • The alignment requirement of the structure is 8 bytes (the strictest alignment requirement of its members), so 6 bytes of padding is needed to bring the total size from 34 bytes to 40.

Thus the size of b is 40 bytes.

The characteristics used are:

  • char has size 1 byte and alignment requirement 1 byte. This is a fixed property of the C standard.
  • short int has size 2 bytes and alignment requirement 2 bytes.
  • float has size 4 bytes and alignment requirement 4 bytes.
  • double has size 8 bytes and alignment requirement 8 bytes. (Having an alignment requirement of 4 bytes instead of 8 would not be very weird; hardware might load and store doubles in 32-bit chunks and not care whether either of them were 8-byte aligned.)
  • The C implementation does not use more padding or stricter alignment than required by the sizes and alignment requirements of structure members. (This is not mandated by the C standard, but there is generally no reason to do otherwise.)
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312