How do struct field alignment and padding work? What are the sizes of these structs?
Question 37HardGo 1.22 to 1.25
type Bad struct {
a bool // offset 0, then 7 bytes padding
b int64 // offset 8
c bool // offset 16, then 7 bytes tail padding
}
type Good struct {
b int64 // offset 0
a bool // offset 8
c bool // offset 9, then 6 bytes tail padding
}
type Tail struct {
x int64
z struct{} // zero-size field at the END still costs space
}
fmt.Println(unsafe.Sizeof(Bad{}), unsafe.Sizeof(Good{}), unsafe.Sizeof(Tail{}))
fmt.Println(unsafe.Offsetof(Bad{}.b), unsafe.Alignof(Bad{}))
Output (64-bit platforms):
24 16 16
8 8
Each field is placed at an offset that is a multiple of its alignment (int64 and pointers use 8 on 64-bit, int32 uses 4, bool uses 1). A struct's alignment is the largest alignment of its fields, and its size is rounded up to a multiple of that, so that elements of an array of the struct stay aligned. The Go compiler never reorders fields, so the declaration order decides how much padding there is. Sorting fields from largest to smallest alignment usually removes most of it.
Gotchas:
- A zero-size field at the end of a struct gets padding. Otherwise
&t.zwould point just past the object, into the next allocation, and keep it alive for the GC. - On 32-bit platforms, 64-bit atomic operations (
atomic.AddInt64) need 8-byte alignment, which is only guaranteed for the first word of an allocated struct. The typedatomic.Int64andatomic.Uint64(Go 1.19+) are always correctly aligned, so prefer them. - The
fieldalignmentanalyzer (ingolang.org/x/tools, or throughgovetin golangci-lint) reports structs that could be smaller. - Layout only matters for memory-heavy data (millions of elements) or for passing memory to C/syscalls. For the latter, Go 1.23 added the
structs.HostLayoutmarker field.
More on Language Fundamentals & Types
- Q35How did loop variable semantics change in Go 1.22, and what does this print?
- Q36How do range-over-func iterators (Go 1.23) work? What does this print?
- Q38What is the difference between a method value and a method expression? What does this print?
- Q39What type does an untyped constant take in a non-constant shift? What does this print?