It's dangerous to code alone! Take this.

Struct Layout: More Than Words

Published on 30 Apr 2022.

In a previous post, we saw that the runtime may place padding between fields when laying out the fields in memory.

I brought up the notion of word sizes, and that word sizes affect access to the underlying data.

Today, I wanted to elaborate on that a bit more, because it isn’t just the word size that matters.

Even some of the diagrams I shared in the last post illustrate this. Even on my computer, which is a 64-bit CPU and a 64-bit computer, we still see the following:

Layout with one byte for A, 3 bytes of padding, then four bytes for B

This is for a struct defined like so:

public struct BoolAndFloat
{
    public bool A;
    public float B;
}

B got shifted by only three bytes, even though my word size is technically eight bytes.

The word size is an important size, but most computers are still quite efficient at extracting data in certain specific subsections of memory smaller than that. For example, grabbing the first four bytes or the last four bytes in an 8-byte word is usually pretty fast. Contrasted with bytes 1 through 4 (in the image above, with 0 being the first) or bytes 3 through 6.

Similarly, grabbing bytes 0 and 1 is fast, as is 2 and 3. But bytes 1 and 2 is slower.

Thus, padding bytes are added for CPU efficiency–and word size is a part of that picture–but the size of the field itself is really the driving force in how many padding bytes will specifically be added.

For any field that, itself, uses four bytes, the runtime will automatically ensure it is aligned on a multiple of four. That’s the reason B, in the image above, starts on byte #4 instead of byte #1.

Similar logic is applied for other sizes as well. If we changed B to be a short, which needs two bytes instead of four, then the runtime would instead ensure that it is aligned on a multiple of 2. It would, therefore, place B in bytes #2 and #3, with only a single byte of padding.

Obviously, if something is only one byte big, then aligning on one-byte boundaries requires no padding bytes at all.

If we go bigger, we see a similar thing with types that need eight bytes. A long or a double, for example, will be automatically aligned to eight bytes, and may have up to seven bytes of padding before them.

I plan on fleshing this topic out a bit more over the coming weeks, so stay tuned for more!