What are zero-sized types' memory semantics? What do these print?
Question 350HardGo 1.22 to 1.25
type T struct {
x int64
z struct{}
}
type U struct {
z struct{}
x int64
}
func main() {
fmt.Println(unsafe.Sizeof(T{}), unsafe.Sizeof(U{}), unsafe.Sizeof([100]struct{}{}))
a, b := new(struct{}), new(struct{})
fmt.Println(a == b) // ???
}
Output: 16 8 0, and then either true or false.
- A zero-sized final field gets padding. Otherwise
&t.zwould point one past the end of the struct, possibly into the next object, and that pointer would keep the wrong object alive. SoTis 16 bytes. Put zero-sized fields first. - Arrays of zero-sized types take no space:
map[K]struct{}sets andchan struct{}signals cost nothing for the values. - The spec says "pointers to distinct zero-size variables may or may not be equal." Heap-allocated zero-size objects usually all point to
runtime.zerobase. Whether these escape changes with how you print them, and the result can differ between compiler versions. Never rely on either answer.
What the interviewer is looking for: you know the tail-padding rule and the unspecified identity of zero-size values. A common bug is using &struct{}{} as a unique sentinel or context key. Use a named non-zero-size type, or an unexported type ctxKey int.
More on Memory, GC & Runtime Internals
- Q348What does this print on a 64-bit platform, and why?
- Q349Explain 64-bit atomic alignment and false sharing. How do you lay out hot concurrent counters?
- Q351What are the rules for valid
unsafe.Pointerusage? - Q352Why is keeping a pointer as
uintptrdangerous even if the object is still referenced elsewhere? - Q353How do you do zero-copy
[]byte↔stringconversion correctly, and what can go wrong? - Q354What does this program print? Explain how finalizers behave.