Why Member Order Matters in C++ Structs?

18 July 2026

The way you arrange members inside a C++ struct can silently bloat your program’s memory footprint. Here is a look at the hidden, low-level rules governing how compilers organize your data.

Introduction: Same Data, Different Size

Consider these two structs:

struct Bad
{
    char c;
    long long x;
    char d;
};

struct Good
{
    long long x;
    char c;
    char d;
};

Both structs store exactly the same information. Do they take up the same amount of space in memory?

sizeof(Bad)  = 24 bytes
sizeof(Good) = 16 bytes

The answer is “No”. The reason has little to do with C++ syntax and much more to do with how modern hardware expects data to be laid out in memory.

Memory Is a Grid, Not a Stream

It is natural to think of memory as a long stream of bytes. This mental model is useful yet incomplete.

fig 1: memory as a long stream of bytes

Modern CPUs do not usually fetch one byte at a time. On a typical 64-bit machine, the hardware usually works with larger chunks, often 8 bytes wide.

fig 2: memory as a grid

That means not every address is equally convenient for every type:

Once you start thinking of memory as a byte grid with preferred starting positions, struct layout stops looking magical.

Why CPUs Care About Alignment

Suppose you want to load an 8-byte “long long”.

If it starts at address 8, or any other address divisible by 8, it is 8-byte aligned. The CPU can fetch the entire value with a single memory read.

fig 3: aligned access

If it starts at address 5, or any other address not divisible by 8, it is misaligned. The CPU may need two memory reads and then combine the relevant bytes to reconstruct the original “long long”.

fig 4: misaligned access

On modern x86 CPUs, unaligned accesses are often allowed and handled for you. But they can still be slower, especially in tight loops or when they cross cache-line boundaries. On other architectures, misaligned access may require multiple instructions or even trap outright. So compilers try hard to keep objects aligned.

C++ Alignment and Padding

Every type in C++ comes with an alignment requirement: a rule about which addresses are valid starting points for objects of that type. On common 64-bit targets, fundamental types often look like this:

Type Typical Size Typical Alignment
char 1 1
short 2 2
int 4 4
long long 8 8
double 8 8

If a type’s alignment is “n”, the compiler places it at an address that, on most mainstream platforms, is usually divisible by “n”. So an 8-byte-aligned “long long” fits naturally at offsets 0, 8, and 16, but not at 1 or 5.

Padding is how the compiler enforces that rule. If the next free byte in a struct is not aligned for the next member, the compiler inserts a few unused bytes until it reaches a valid offset.

How Poor Member Order Creates Internal Padding

Let’s go back to the “struct Bad” and see how it is laid out in memory.

struct Bad
{
    char c;
    long long x;
    char d;
};

So the three members themselves account for 17 bytes. But the layout is still not finished.

Why the Compiler Adds Trailing Padding

The compiler also has to think about arrays, not just one object. This becomes easier to see when you place two “struct Bad” objects in an array:

Bad items[2];

If “sizeof(Bad)” were 17, the second element would begin at offset 17:

items[0] starts at 0
items[1] starts at 17

That would place “items[1].x” at offset 25, which is not divisible by 8. In other words, the “long long” inside the second object would be misaligned.

A struct has an alignment requirement of its own, usually set by its most strictly aligned member. Here, “Bad” contains a “long long”, so the struct itself needs 8-byte alignment. To make sure every element in an array of “Bad” starts on an 8-byte boundary, the compiler rounds the total size of the struct up to a multiple of 8 by adding extra bytes at the end.

fig 5: layout of struct Bad

That extra space is called trailing padding. It is not there to help “d”. It is there so the next “Bad” object can begin at a properly aligned address.

That is why “sizeof(Bad)” becomes 24 instead of 17. The key idea is simple: trailing padding mostly exists to protect the next object in memory.

How Better Member Order Eliminates Internal Padding

Now compare that with “struct Good”, which contains the same members in a better order.

struct Good
{
    long long x;
    char c;
    char d;
};

fig 6: layout of struct Good

The key improvement here is that there is no internal padding. By placing the most strongly aligned member first, “struct Good” avoids the 7-byte internal padding that appeared in “struct Bad”. Some padding still remains at the end, but it is only trailing padding, so the total size drops from 24 bytes to 16.

Why This Matters in Real Code

An 8-byte saving sounds small until the struct shows up everywhere.

And memory cost is only part of it.

This matters in systems code, game engines, databases, browsers, networking stacks, and any codebase that keeps large collections of objects alive.

It also matters in class hierarchies. If a padded base class carries dead space, every derived object inherits that wasted layout. One sloppy base type can quietly multiply memory bloat across an entire subsystem.

Takeaways

Here is the mental model to keep:

And here is the rule of thumb:

Order struct members by decreasing alignment requirement, usually from largest scalar types to smallest.

It is not a magical law. Exact layout depends on the target platform, the compiler, and the types involved. But as a default habit, it is one of the easiest low-level wins you can get.

© 2026 SM Mehrab

This article by SM Mehrab is licensed under CC BY 4.0.

You may share and adapt this article if you give proper credit to the original author.