go普通指针、unsafe.Poniter、unintptr之间的联系
GO指针
. 普通指针 *type 普通指针,用于传递对象地址,不能进行指针运算
. unsafe.Poniter 通用型指针,用于不同类型指针的转换,不能进行指针运算,不能读取内存存储的值
. unintptr 可进行指针运算,由于其无法持有对象,GC不把unintptr当作指针,unintptr常被回收
unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。
只有uintptr类型才可以进行运算,只要将普通指针类型转换成uintptr类型,做完加减法后,再转换成普通指针,通过*操作,取值,修改值,随意。
总结:unsafe.Pointer 可以让你的变量在不同的普通指针类型转来转去,也就是表示为任意可寻址的指针类型。而 uintptr 常用于与 unsafe.Pointer 打配合,用于做指针运算
unsafe.Pointer
- 用于各种指针相互转换的桥梁,它可以包含任意类型变量的地址
- 当然,我们不可以直接通过*p来获取unsafe.Pointer指针指向的真实变量的值,因为我们并不知道变量的具体类型。
- 和普通指针一样,unsafe.Pointer指针也是可以比较的,并且支持和nil常量比较判断是否为空指针。
uintptr
- uintptr是一个整数类型。即使uintptr变量仍然有效,由uintptr变量表示的地址处的数据也可能被GC回收,这个需要注意!。
unsafe包
-unsafe包只有两个类型,三个函数
type ArbitraryType int
type Pointer *ArbitraryType
func Sizeof(x ArbitraryType) uintptr //返回其占用的字节数
func Offsetof(x ArbitraryType) uintptr //返回结构体中元素所在的内存的偏移量
func Alignof(x ArbitraryType) uintptr //返回变量对齐字节数量
-三个函数的参数均是ArbitraryType类型,就是接受任何类型的变量。
运用
unsafe.pointer用于普通指针类型转换
v1 := int(12)
p = (*uint)(unsafe.Pointer(&v2))
unsafe.pointer用于访问操作结构体的私有变量
type V struct {
i int32
j int64
}
func main() {
var v *V = new(V)
var i *int32 = (*int32)(unsafe.Pointer(v))
*i = int32(98)
var j *int64 = (*int64)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + uintptr(unsafe.Sizeof(int32(0)))))
*j = int64(763)
v.PutI()
v.PutJ()
}
func main() {
var b []byte = []byte{'a', 'b', 'c'}
var c *byte = &b[0]
fmt.Println(*(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(c)) + uintptr(1))))
}
//错误写法,不要试图引入一个uintptr类型的临时变量,因为它可能会破坏代码的安全性,随时可能被GC回收影响
tmp := uintptr(unsafe.Pointer(c)) + uintptr(1)
pb := (*byte)(unsafe.Pointer(tmp))
*pb = 'd'