Go

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.z would 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 typed atomic.Int64 and atomic.Uint64 (Go 1.19+) are always correctly aligned, so prefer them.
  • The fieldalignment analyzer (in golang.org/x/tools, or through govet in 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.HostLayout marker field.

More on Language Fundamentals & Types

All 39 Language Fundamentals & Types questions