关于三者的解释
- *type 表示具体的指针类型;例如 *int,*string,*structName
- unsafe.Pointer 想到于c++中的 void*,是所有指针类型的基类。
任何类型的指针都可以被转化为Pointer;
Pointer可以被转化为任何类型的指针。 - uintptr:官方解释
// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr
这里可以看到uintptr其实就是一个可以保存指针指向地址的整数,简单说就是:uintptr是一个可以加减计算的整数。
为什么有了 *星号 和 unsafe.Pointer 还要uintptr呢?因为 前两者都不可以直接进行加减运算,在需要做地址偏移offset运算的时候就需要 uintptr 来辅助了。
看一段代码,使用这三个东西来修改结构的私有变量,因为结构体的私有变量在外面是不可以拿到的,那么就要整一些骚操作了,我们直接通过指针偏移去修改里面的每一个值。
type Person struct {
age int
name string
}
func main() {
var zhangsan Person = Person{25,"zhangsan"}
// 因为结构体变量的起始地址就是第一个成员变量的地址,所以先使用基类型指针unsafe.Pointer
// 取到结构体变量的首地址,然后就像子类转成基类一样,把unsafe.Pointer转成具体类型*int
p := (*int)(unsafe.Pointer(&zhangsan))
*p = 35
// 这里的逻辑就是把首地址向后偏移一个int的字节大小就行了,因为unsafe.Pointer是不能直接进行加减
// 计算的,所以要先转成可计算的int类型即uintptr,也就是把zhangsan的地址给到uintptr让它去代劳计算
// unsafe.Sizeof返回值就是 uintptr;计算得到的结果再给到 unsafe.Pointer 指向偏移后的地址;
// 最后再转到具体的指针类型 *string,就可以修改私有变量了
q := (*string)( unsafe.Pointer( uintptr(unsafe.Pointer(&zhangsan))+ unsafe.Sizeof(int(0))) )
*q = "lisi"
fmt.Printf("%v",zhangsan)
}