StructLocalVariable.c — a struct on the stack

OST2 Arch1001 · accessing a struct local variable · same program as 004, but the locals are caged in a struct — and this build packs it
RIP = 00000001`40001000 — no instruction yet executed
☒executed instruction
♍modified value
⌘start value
RIP▶next instruction
Δ004only the displacement differs from ArrayLocalVariable

C source

Disassembly

⤴ execution continues at 00000001`40001379 — the code that called main()

Registers

rax
rcx
rsp

Stack (4-byte granularity — values straddle the rows!)

↑higher addresses
lower addresses (stack grows down)↓
rsp

Packed layout: foo.a @ rsp+0 · foo.b[i] @ rsp+2+4i · foo.c @ rsp+1Ah. Nothing is 4- or 8-byte aligned, so every value spills across row boundaries.

←/→ step · Space play/pause · Home/End jump

StructLocalVariable.c takeaways

typedef struct mystruct{ short a; int b[6]; long long c; } mystruct_t;

Upside down, the declaration reads c → b → a top-to-bottom — exactly the order the stack diagram shows them.

The plot twist — expected layout vs. what the disassembly says

Draw the struct by MS' documented alignment & padding rules and you get the left diagram. But the disassembly stores c at [rsp+1Ah] and bases b at +2 — this build used 1-byte structure packing (#pragma pack(1) / /Zp1), so the real layout is the right diagram: no padding anywhere, nothing aligned, values straddling the 4-byte rows.

Expected — default alignment (/Zp8): members aligned to their size, struct padded out. sizeof = 0x28.
0014FE08
return address = 00000001`40001379
0014FE00
16-byte-stack-alignment padding
0014FDFC
undef (alignment padding)
0014FDF8
undef (alignment padding)
0014FDF4
c (MSBs) = 0ba1b0ab
0014FDF0
c (LSBs) = 1edb100d
0014FDEC
undef (struct alignment padding)
0014FDE8
b[5] = undef
0014FDE4
b[4] = 1edacacb
0014FDE0
b[3] = undef
0014FDDC
b[2] = undef
0014FDD8
b[1] = ffffbabe
0014FDD4
b[0] = undef
0014FDD0
padding, a = babe (2 bytes)
Actual — packed (/Zp1): zero padding inside the struct. sizeof = 0x22 (2 + 24 + 8). Members split across rows.
0014FE08
return address = 00000001`40001379
0014FE00
16-byte-stack-alignment padding
0014FDFC
undef (16 byte alignment padding)
0014FDF8
undef (16 byte alignment padding)
0014FDF4
undef (16 byte alignment padding)
0014FDF0
pad · c 2 MSBs = 0ba1
0014FDEC
c 4 middle bytes = b0ab1edb
0014FDE8
c 2 LSBs = 100d · b[5] MSBs
0014FDE4
b[5] LSBs · b[4] 2 MSBs = 1eda
0014FDE0
b[4] 2 LSBs = cacb · b[3] MSBs
0014FDDC
b[3] LSBs · b[2] MSBs
0014FDD8
b[2] LSBs · b[1] 2 MSBs = ffff
0014FDD4
b[1] 2 LSBs = babe · b[0] MSBs
0014FDD0
b[0] LSBs · a = babe

MS' struct alignment & padding rules — the 60-second version

From the x64 software conventions doc: scalar types align to their own size; each member is placed at the next multiple of min(its alignment, the packing level); the whole struct's size is rounded up to a multiple of its strictest member's alignment.

membersizenatural alignoffset @ /Zp8 (default)offset @ /Zp1 (this binary)
short a220x000x00
int b[6]244 (per element)0x04 (2 pad bytes)0x02
long long c880x20 (4 pad bytes)0x1A
sizeof(mystruct_t)0x28 (40)0x22 (34)

Frame-size coincidence: 0x28 and 0x22 both round up to 0x30 in 16-byte chunks, + 8 alignment = sub rsp, 38h either way — you can't tell the packing from the prologue, only from the member displacements.