34 Intermediate The Hundred
The Struct That Shrank
same three members, different order, different bill
struct Wasteful {
char a;
int b;
char c;
};
struct Tidy {
int b;
char a;
char c;
};
int main() {
std::cout << "sizeof(Wasteful) = " << sizeof(Wasteful) << " a@" << offsetof(Wasteful, a)
<< " b@" << offsetof(Wasteful, b) << " c@" << offsetof(Wasteful, c) << '\n';
std::cout << "sizeof(Tidy) = " << sizeof(Tidy) << " b@" << offsetof(Tidy, b) << " a@"
<< offsetof(Tidy, a) << " c@" << offsetof(Tidy, c) << '\n';
}
Run it. Two structs, the same three members — the same size?
Answer
No. Shuffling three declarations cut a third off the struct (GCC 11.5, x86-64):
Why¶
Every type has an alignment — on x86-64 an int wants an address divisible by 4 —
and the compiler may not reorder members, so its only tool is padding. Wasteful puts
a at 0, burns three bytes so b can start at 4, drops c at 8, then pads the tail to
12 to keep the size a multiple of its 4-byte alignment. Tidy leads with the int and
both chars slot in behind it at 4 and 5 — 8 bytes, still not 6, because tail padding
never goes away. Those padding bytes hold whatever garbage was there, so std::memcmp
can report two logically equal objects as different. -Wall -Wextra says nothing, but
add -Wpadded and GCC narrates every hole: "padding struct to align 'Wasteful::b'".
The fix¶
Declare members from largest alignment down to smallest and the holes mostly close up:
struct Tidy {
int b; // widest first, then the small stuff fills in behind it
char a;
char c;
}; // 8 bytes, not 12
#pragma pack(push, 1) squeezes the original to 6 bytes, but you pay: unaligned access
is slower on x86-64 and illegal on some architectures. (alignas(1) is no escape hatch:
it cannot weaken a type's natural alignment — GCC 11.5 leaves the struct at 12 bytes.)
Takeaway: member order is part of the layout — the compiler pads, it never reorders.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo