Assembly Breakdown: x86–64 structure alignment examples
x64 Structure Alignment in MSVC: What the Compiler Does to Your Structs
Assembly Breakdown: x86–64 structure alignment examples

x64 Structure Alignment in MSVC: What the Compiler Does to Your Structs
When you declare a struct in C, the compiler does not simply pack the members back-to-back in memory. It inserts padding bytes to ensure each member sits at an address that is a multiple of its own size. The x64 ABI formalises this as natural alignment: a short must be 2-byte aligned, an int 4-byte aligned, a double or pointer 8-byte aligned. The struct's total size is then rounded up to a multiple of its largest member's alignment, so that arrays of structs remain correctly aligned at every element.
The Microsoft documentation illustrates this with four concrete examples. A struct containing only a short needs no padding at all -- two bytes, done. Add an int followed by a double followed by a short, and the picture changes: four bytes of padding appear between the int and the double to push the double to an 8-byte boundary, and six bytes of trailing padding follow the short to bring the total to 24. A struct with interleaved char, short, char, and int members accumulates internal padding in two places, wasting 4 bytes out of 12. The union example shows the other side of the coin: all members start at offset 0, the total size is simply the largest member, and smaller members leave the high bytes unused.
Two practical points follow from this.
- First, member ordering is not neutral. Declaring fields largest-to-smallest eliminates most internal padding; the interleaved-type example shrinks from 12 bytes to 8 if reordered.
- Second, Rust’s default
#[repr(Rust)]layout allows the compiler to reorder fields freely to minimise padding, so a Rust struct in a binary will not necessarily match the source declaration order. Only#[repr(C)]guarantees the C-compatible layout described above, which matters when reconstructing types during reverse engineering.
The four examples demonstrate the two core alignment rules: each member sits at an offset that is a multiple of its own size, and the total struct size is padded to a multiple of the struct’s alignment (which equals the largest member’s alignment).
Let’s review the examples from Microdoft Doc.
Part one is based on Microsoft Doc. Check it out: https://learn.microsoft.com/en-us/cpp/build/x64-software-conventions?view=msvc-170
Example 1: trivial, no padding
_declspec(align(2)) struct {
short a; // +0; size = 2 bytes
}
// Total: 2 bytes, alignment: 2 bytes
short requires 2-byte alignment. Placed at offset 0, which satisfies that. Total size 2 is already a multiple of 2. No padding needed anywhere.
Example 2 : internal and trailing padding
_declspec(align(8)) struct {
int a; // +0; size = 4 bytes
double b; // +8; size = 8 bytes
short c; // +16; size = 2 bytes
}
// Total: 24 bytes, alignment: 8 bytes
a is an int (4 bytes, 4-byte aligned) — placed at offset 0, no issue. b is a double (8 bytes, 8-byte aligned) — the next available offset after a is 4, which is not a multiple of 8, so 4 bytes of padding are inserted at offsets 4–7 to push b to offset 8. c is a short (2 bytes, 2-byte aligned) — placed at offset 16, aligned correctly. That leaves offsets 18–23 as trailing padding: the struct's alignment is 8 (driven by double b), so total size must be a multiple of 8. 18 rounded up to the next multiple of 8 = 24. Six bytes of trailing padding are added.
The trailing padding matters when the struct is used in an array: arr[1] must also start at an 8-byte boundary, which requires each element to be 24 bytes.
Example 3: interleaved padding from mixed types
_declspec(align(4)) struct {
char a; // +0; size = 1 byte
short b; // +2; size = 2 bytes
char c; // +4; size = 1 byte
int d; // +8; size = 4 bytes
}
// Total: 12 bytes, alignment: 4 bytes
Offset │ 0 1 2 3
───────┼────────────────────
│ a [p] b b
Offset │ 4 5 6 7
───────┼────────────────────
│ c [p] [p] [p]
Offset │ 8 9 10 11
───────┼────────────────────
│ d d d d
ais acharat offset 0 : 1-byte aligned, no padding needed.bis ashort: requires 2-byte alignment; offset 1 is odd, so 1 byte of padding inserted beforeb, placing it at offset 2.cis acharat offset 4 .dis anint: requires 4-byte alignment; the next offset aftercis 5, which is not a multiple of 4, so 3 bytes of padding inserted befored, placing it at offset 8.- Total size is 12, which is a multiple of 4 (the struct's alignment). No trailing padding needed.
This example illustrates why member ordering matters: reordering to {int d; short b; char a; char c;} would produce a 8-byte struct with no internal padding at all.
Example 4: union, all members at offset 0
_declspec(align(8)) union {
char *p; // +0; size = 8 bytes
short s; // +0; size = 2 bytes
long l; // +0; size = 4 bytes
}
// Total: 8 bytes, alignment: 8 bytes
Offset │ 0 1 2 3 4 5 6 7
───────┼────────────────────────────────────
p │ p p p p p p p p (all 8 bytes)
s │ s s [p] [p] [p] [p] [p] [p] (2 bytes + 6 padding)
l │ l l l l [p] [p] [p] [p] (4 bytes + 4 padding)
All union members share offset 0 — that is the defining property of a union. The total size is determined by the largest member (char* = 8 bytes on x64), and the alignment is determined by the largest alignment requirement, also 8 (char* is a pointer = quadword). s and l occupy only the low bytes of the 8-byte slot; the remainder is unused padding within the union's footprint.
RE Relevance
Struct layout rules are directly relevant to binary analysis of Rust code. Rust’s compiler (via LLVM) applies the same natural alignment rules by default for #[repr(C)] types. For #[repr(Rust)] types, the compiler is free to reorder fields to minimise padding — which means a Rust struct in a malware sample will not necessarily have the same field ordering as the declared source, complicating type reconstruction in Binary Ninja or IDA. Recognising padding bytes versus data bytes is essential when recovering struct layouts from MLIL/HLIL.
Microsoft Corporation (2023) x64 ABI conventions. Microsoft Learn. Available at: https://learn.microsoft.com/en-us/cpp/build/x64-software-conventions
메타데이터
- post_id
- a40a0ef325e5
- slug
- assembly-breakdown-x86-64-structure-alignment-examples-a40a0ef325e5
- url
- https://medium.com/re-exploit/assembly-breakdown-x86-64-structure-alignment-examples-a40a0ef325e5
- canonical_url
- https://medium.com/re-exploit/assembly-breakdown-x86-64-structure-alignment-examples-a40a0ef325e5
- author_url
- https://medium.com/@MonlesYen
- status
- ok
- fetched_at
- 2026-07-10 19:15:58