Go | unsafe package
1. 简介
unsafe不安全包,可以为我们提供一些Go语言底层的操作。
如:
- 查找变量占用空间
- 内存对齐倍数
- 指针运算
- 访问非公开的变量
注意:正常我们在开发Go语言程序,应该尽量避免使用该包的内容。
2. 常用方法
2.1. unsafe.Sizeof
简介
计算变量内存中占用字节大小。
示例
func main() {
var i int32 = 123
fmt.Println(unsafe.Sizeof(i)) // Output: 4
fmt.Println(unsafe.Sizeof(&i)) // Output: 8
}
注意
func main() {
e := struct {
A *int32
}{}
size := unsafe.Sizeof(e)
fmt.Println(size) // Output: 8
}
2.2. unsafe.Offsetof
简介
计算结构体指针到当前字段内存地址的偏移量,还有可能包含内存对齐产生的空洞。
示例
func main() {
e1 := struct {
A int32
B int32
}{
A: 1,
B: 2,
}
offset1 := unsafe.Offsetof(e1.B)
fmt.Println(offset1) // Output: 4
e2 := struct {
A uint8
B int32
}{
A: 1,
B: 2,
}
offset2 := unsafe.Offsetof(e2.B)
fmt.Println(offset2) // Output: 4
}
2.3. unsafe.Alignof
简介
返回参数需要对齐的倍数。
示例
func main() {
e := struct {
A uint8
B uint32
}{}
fmt.Println(unsafe.Alignof(e)) // Output: 4
fmt.Println(unsafe.Sizeof(e)) // Output: 8
}
2.4. unsafe.Add
简介
指针运算,可以使用usafe.Add进行指针运算达到C/C++指针运算效果。
通过指针运算还可以访问结构体内未开放访问的成员。
示例
func main() {
example := struct {
A uint32
B uint32
c uint32
}{
A: 100,
B: 101,
c: 102, // 注意: 小写也可以访问
}
a := unsafe.Add(unsafe.Pointer(&example), 0)
fmt.Println(*(*uint32)(a)) // Output: 100
b := unsafe.Add(a, 4) // uint32 4个字节
fmt.Println(*(*uint32)(b)) // Output: 101
c := unsafe.Add(b, 4) // uint32 4个字节
fmt.Println(*(*uint32)(c)) // Output: 102
}
遍历slice
// slice 内部实现
type slice struct {
array unsafe.Pointer // 8 字节
len int // 8 字节
cap int // 8 字节
}
func main() {
arr := make([]uint32, 2, 4)
arr[0] = 100
arr[1] = 101
// 当前指指针在结构体头
slicePoint := unsafe.Pointer(&arr)
// 将指针移动到len启始位置
lenPoint := unsafe.Add(slicePoint, 8)
length := *(*int)(lenPoint)
fmt.Println(length) // Output: 2
// 将指针移动到cap启始位置
capPoint := unsafe.Add(lenPoint, 8)
capacity := *(*int)(capPoint)
fmt.Println(capacity) // Output: 4
i := *(*int)(slicePoint)
p := unsafe.Pointer(uintptr(i))
// 数组中每个元素
for i := 0; i < length; i++ {
item := unsafe.Add(p, i*4)
fmt.Println(*(*uint32)(item)) // Output: 100 101
}
}