go 不像c 那样直接可以对指针做一些操作,所以go 提供了一个标准的package 来操作指针。go 有三种指针类型:
1. * 普通指针, 用于传递对象地址,不能进行指针运算
2. unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算。
3. uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收
unsafe.Pointer 可以和 普通指针 进行相互转换, unsafe.Pointer 可以和 uintptr 进行相互转换; 也就是说 unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算
我们可以看下面的code:
package main
import (
"fmt"
"unsafe"
)
func main() {
s := struct {
a byte
b byte
c byte
d int64
}{0, 0, 0, 0}
p := unsafe.Pointer(&s)
up0 := uintptr(p)
pb := (*byte)(p)
*pb = 10
fmt.Println(s) // 10, 0, 0 , 0
up := up0 + unsafe.Offsetof(s.b)
p = unsafe.Pointer(up)
pb = (*byte)(p)
*pb = 20
fmt.Println(s) //10, 20, 0, 0
up = up0 + unsafe.Offsetof(s.c)
p = unsafe.Pointer(up)
pb = (*byte)(p)
*pb = 30
fmt.Println(s) // 10, 20, 30. 0
up = up0 + unsafe.Offsetof(s.d)
p = unsafe.Pointer(up)
pi := (*int64)(p)
*pi = 40
fmt.Println(s) //10, 20, 30, 40
}
每种类型都有它的大小和对齐值,可以通过 unsafe.Sizeof 获取其大小,通过 unsafe.Alignof 获取其对齐值,通过 unsafe.Offsetof 获取其偏移量。不过 unsafe.Alignof 获取到的对齐值只是该类型单独使用时的对齐值,不是作为结构体字段时与其它对象间的对齐值,这里用不上,所以需要用 unsafe.Offsetof 来获取字段的偏移量,进而确定其内存地址