Go specification makes very few address alignment guarantees
Go specification only specifies a little on address alignment guarantees:
The following minimal alignment properties are guaranteed:
- For a variable x of any type: unsafe.Alignof(x) is at least 1.
- For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe.Alignof(x.f) for each field f of x, but at least 1.
- For a variable x of array type: unsafe.Alignof(x) is the same as the alignment of a variable of the array's element type.
A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.
But the popular Go compilers, gc
(the one in Go Toolchain) and gccgo
, make more address alignment guarantees.
They both make the following guarantees:
type alignment guarantee (bytes)
------ ------
bool, byte, uint8, int8 1
uint16, int16 2
uint32, int32 4
float32, complex64 4
arrays depend on element types
structs depend on field types
other types size of a native word (4 for 32-bit arch, 8 for 64-bit arch)
Some standard packages comply with the guarantees made by gc
and gccgo
instead of Go specification. For example, the implementation of sync.WaitGroup
assume the address alignment guarantee of uint32
values is 4
.
At least up to Go 1.13, the 64-bit atomic functions provided in the sync/atomic
standard package require the operated arguments must be 8-byte aligned, so the following program might panic on 32-bit OSes, because the address of v.y
is not guaranteed to be 8-bytes aligned.
package main
import "sync/atomic"
type T struct {
x int32
y int64
}
func main() {
var v T
_ = atomic.LoadInt64(&v.y)
}
For the same reason, the following program might also panic in running.
The reason is the addresses of the i
field of v.y
and the f
field of v.z
might not be 8-byte aligned on 32-bit OSes, but their Add
method applies
64-bit atomic functions to the two fields.
package main
import "expvar"
type T struct {
x int32
y expvar.Int
z expvar.Float
}
func main() {
var v T
v.y.Add(1)
v.z.Add(0.1)
}
There are some situations compilers should guarantee 64-bit numeric values are 8-byte aligned on any arch/OS.
There is a proposal which proposes 64-bit numeric values should be always 8-byte aligned. At present (Go 1.13), the proposal has not been accepted yet.
(Update: although the just mentioned proposal is not accepted yet, since Go 1.19, two types, atomic.Int64 and atomic.Uint64, has been added to Go. Values of the two types are always 8-byte aligned, even on 32-bit architectures.)
Please follow the official Twitter account of Go 101, @go100and1, to learn some Go details, facts, tips, etc, and read some Go articles from time to time.
Go 101 is a series of books about Go programming.
Books in Go 101 series
- Go (Fundamentals) 101 focuses on Go syntax/semantics (except custom generics related) and all kinds of runtime related things.
- Go Optimizations 101 provides some code performance optimization tricks, tips, and suggestions.
- Go Details & Tips 101 collects many details and provides several tips in Go programming.
- Go Generics 101 explains Go custom generics in detail.
Tapir started writing the Go 101 books and maintaining the go101.org website since 2016. New contents will continue being added to the books and the website from time to time. If you would like to, you can also support Go 101 by playing Tapir's games (for both Android and iPhone/iPad):
- Color Infection (★★★★★) - a physics based casual puzzle original game. 140+ levels.
- Rectangle Pushers (★★★★★) - a casual puzzle original game. 104+ levels.
- Let's Play With Particles - a casual action original game. Three game modes are included.