3个指针
*T
常用类型指针类型,用于传递对象地址,不能进行指针运算。
unsafe.Pointer
通用指针类型,用于转换不同类型的指针,不能进行指针运算,不能读取内存存储的值(需转换到某一类型的普通指针)
uintptr
用于指针运算,GC不把uintptr当指针,uintptr无法持有对象。uintptr
类型的目标会被回收
3者关系
unsafe.Pointer
是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为uintptr
进行指针运算,也就说uintptr
是用来unsafe.Pointer
互相配合
常用方法
unsafe.Sizeof
unsafe.Sizeof 报告传递给它的参数在内存中所占的字节长度(后面修改结构体用到)
unsafe.Offsetof()
计算成员相对于结构体的起始地址的偏移量
使用unsafe修改私有变量
//其他包引入需要Sizeof方法算出大小 偏移地址//同一个包下 直接使用Offsetof方法p :=model.Programmer{} fmt.Println(p) name := (*string)(unsafe.Pointer(&p)) *name="xixi"age := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) +unsafe.Sizeof(string("")))) *age=18lan := (*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) +unsafe.Sizeof(string("")) +unsafe.Sizeof(int(0)))) *lan="golang"fmt.Println(p)
使用unsafe获取修改slice和map的长度
s :=make([]int, 9, 20) Len := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) +uintptr(8))) *Len=10//获取长度//Len := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)))Cap := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) +uintptr(16))) *Cap=10//获取容量//Cap := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(16)))fmt.Println(len(s), cap(s)) //fmt.Println(Len, Cap)
面试考点(字符串转成byte数组,会发生内存拷贝吗?)如何实现0拷贝
str :="hello world"sptr := (*reflect.StringHeader)(unsafe.Pointer(&str)) slice :=*(*[]byte)(unsafe.Pointer(sptr)) fmt.Println(slice)
总结
用unsafe包来操作内存,增加了go语言的灵活性,但同时也增加了操作风险