golang Unsafe Alignof explained

Last Modified: 2023/08/11

概述

本文介绍 golang unsafe 包中的 Alignof 方法的用法,这对理解 unsafe 包中另一个方法 Sizeof 至关重要,let's go。

Go Alignof 的含义

对于任意表达式 x,当调用 Align(x) 的时候就相当于你定义了一个虚拟变量 v,然后将 x 赋值给 v,Alignof(x) 返回的值就是这个虚拟变量 v 的内存对齐要求。换句话说对齐 v 就是使: v 的地址 mod Alignof(v) == 0,同时 Alignof(v) 取最大可能的值。

那么按照上面的说法,如果 v 的地址是 100,那么最大的值是 100 吗?其实并不是这样,这个最大值是平台相关的,64 位系统,最大值为 64 / 8 = 8 字节,32 为系统为 4 字节。

对齐的目的是为了更高效的读取,CPU 总是以字(WORD)为单位读取,字的大小是平台相关的,64 位系统,字大小为 8 字节,32 位系统字大小为 4。试想去读一个整形变量,如果不对齐,那么该整形可能跨越两个字,需要两次读取,如果对齐了,只需要一次读取,因为一个整形的大小正好和字大小相同。

对齐保证

Go 语言规范中,只规定了以下最小的对齐保证:

  • 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.

简单翻译就是:任何类型变量 x, 对齐要求至少为 1;对于 struct 类型的变量 x,x 中包含的字段为 f1 到 fn,那么 unsafe.Alignof(x) = max(unsafe.Alignof(x.f1), unsafe.Alignof(x.f2), ..., unsafe.Alignof(x.fn)),换句话说 struct 类型的变量 x 的对齐要求为 x 的所有字段的对齐要求的最大值;数组类型的变量 x 的对齐要求是由数组中元素类型的对齐要求决定的。

以下是 Go 标准编译器的对齐保证:

No. type 对齐保证
1 bool, uint8, int8 1
2 uint16, int16 2
3 uint32, int32 4
4 float32, complex64 4
5 arrays 由元素类型决定
6 structs 由字段类型决定
7 其他类型 由平台 WORD 大小决定,即 64 位系统为 8,32 位系统为 4

No. 列是编号,下面的例子中会引用该编号以便让大家更好理解 Alignof 的输出依据的是哪条规则。

Golang Alignof 实战

注:以下例子均在 64 位操作系统上产生的结果。

输入 Alignof No.
int8(0) 1 1
true 1 1
int16(0) 2 2
int32(0) 4 3
int64(0) 8 7
int(0) 8 7
[2]int{0, 1} 8 5

依据第 7 条规则,Alignof(int(0)) 输出结果为 8。需要注意的是:如果在 32 位系统上,输出结果为 4。

由于 Go 只规定了最小的对齐保证,大部分未规定类型的变量的 Alignof 要么是 8,要么是 4。因此下面的输出结果均为 8。需要注意 []int8 {1} 是切片,不是数组,所以不能依据规则 5,而是应该依据规则 7。

// 8
println(unsafe.Alignof(""))
// 8
println(unsafe.Alignof("abc"))
// 8
println(unsafe.Alignof([]int8 {1}))
// 1
println(unsafe.Alignof([1]int8 {1}))

结构体的 Alignof 稍微复杂,结构体的对齐取决于所有字段对齐保证的最大值,套下面的公式即可:

unsafe.Alignof(x) = max(unsafe.Alignof(x.f1), unsafe.Alignof(x.f2), ..., unsafe.Alignof(x.fn))

假设一个结构体定义如下:

type M struct {
	x int8
	y int32
	z int64
}
m := M{}
// 输出 8
println(unsafe.Alignof(m))

unsafe.Alignof(m) = max(unsafe.Alignof(m.x), unsafe.Alignof(m.y), unsafe.Alignof(m.z)) = max(1, 4, 8) = 8

总结

Golang Alignof 并不复杂,只要记住最小对齐保证即可。

有问题吗?点此反馈!

温馨提示:反馈需要登录