Open 981377660LMT opened 4 months ago
可以看到 unsafe.Pointer 其实就是一个 *int,一个通用型的指针。
我们看下关于 unsafe.Pointer 的4个规则。
任何指针 都可以转换为 unsafe.Pointer unsafe.Pointer 可以转换为 任何指针 uintptr 可以转换为 unsafe.Pointer unsafe.Pointer 可以转换为 uintptr
unsafe.Pointer在unsafe包中还有以下几个函数在我们计算可能会用到 ==unsafe包的几个函数==
//返回一个变量占用的内存字节数
func Sizeof(x ArbitraryType) uintptr
//返回结构体某个字段的地址相对于此结构体起始地址的偏移量
func Offsetof(x ArbitraryType) uintptr
//返回对齐系数
func Alignof(x ArbitraryType) uintptr
a := int64(100)
var ptr *int
ptr = (*int)(unsafe.Pointer(&a))
fmt.Printf("%d\n", *ptr) //输出100 如果是int32输出一个很大值
// 如果是结构体的话,我们可以用到unsafe.Offsetof函数来计算 type People struct { age int32 height int64 } var p = new(People) height := unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Offsetof(p.height)) ((int)(height)) = 100 //将height的值改为100 //uintptr(unsafe.Pointer(p)) 获取了 w 的指针起始值 //unsafe.Offsetof(w.p) 获取 b 变量的偏移量
- uintptr和unsafe.Pointer的转换
```go
var p = &People
height := unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Offsetof(p.height))
*((*int)(height)) = 100 //将height的值改为100
四、unsafe.Pointer强制转换 介绍这个方式之前,需要了解下go的指针。在go语言中有3类指针: 普通指针、uintptr、unsafe.Pointer。
普通指针 (T): 存储类型T的对象的地址,可以使用&(取地址)(根据地址取值),但不可计算 uintptr:一个无符号的整型,可以保存一个指针地址,可以进行计算。uintptr无法持有对象,GC不把uintptr当指针,所以uintptr类型的目标会被回收 unsafe.Ponter: 可以指向任意类型的指针,不能进行计算,不能读取内存存储的值 我们知道,指针的本质是一个uint类型,存储对象的地址(地址其实也是一个数字),在C语言中指针可以任意的计算、指向,因而存在比较大的风险。go语言为了避免指针被滥用、误用的风险,对指针做了限制(如类型校验、不可计算、不可跨类型转换等)。但同时也留了一个口子,允许指针进行跨类型转换,这便是unsafe.Pointer(从unsafe可以看出这个指针不安全的,使用不当容易出事)。
三类指针的特性如下:
普通指针(T)不可计算、不可转换 unsafe.Pointer可以和任意类型指针转换(T, uintptr, unsafe.Pointer) uintptr可以计算 因而借助unsave.Pointer、uintptr,我们可以实现对普通指针进行计算和跨类型转换。这第四种类型转换便是借助unsafe.Pointer可任意转换的能力来实现。
跨类型类型转换: newPtr := (newT)(unsave.Pointer(ptr)) 先转为unsafe.Pointer,再转为目标类型指针 ptr -> unsafe.Pointer -> newT 指针计算: 先转为unsafe.Pointer,再转为uintptr,进行指针计算,再转为unsafe.Pointer,最后转为目标普通指针 ptr -> unsafe.Pointer -> uintptr -> 指针计算 -> unsafe.Pointer -> ptr
Go 指针、uintptr、unsafe.Pointer之间如何转换
unsafe.Pointer作为桥梁实现相互转换 普通指针 <=> unsafe.Pointer <=> uintptr