普通指针和unsafe.pointer类型的指针都能代表引用一个地址,被gc发现。但是uintptr是不代表引用一个地址的。不能被gc发现,如果没有其他引用可能会被回收。
作用:在函数传参时修改原值
如下,将 *int 类型指针转化为 *float 类型
var p1* int var p2 unsafe.pointer var p3* float64 p2 = unsafe.pointer(p1) p3 = (*float64)(p2) log.println(reflect.typeof(p3))//*float64
uintptr类型可以进行指针运算,uintptr是个整数,内存地址编号
内存地址可以看做是一个线性的字节数组。按字节编址,存储单元(8位一个单元)都有一个编号,
name :="biningo" namep:=unsafe.pointer(&name) namept:=uintptr(namep) log.printf("%x %x\n",&name,namept) //c000054200 c000054200
uintptr运算:
arr:=[5]int64{1,2,3,4,5} uptr:=uintptr(unsafe.pointer(&arr)) log.printf("%x %x\n",uptr,&arr[0]) uptr +=uintptr(8) log.printf("%x %x\n",uptr,&arr[1]) arr2:=*(*int64)(unsafe.pointer(uptr)) log.println(arr2) //2 arr2=100 log.println(arr[1]) //2 不变 arr3:=(*int64)(unsafe.pointer(uptr)) *arr3 = 999 log.println(arr[1]) //999 改变
下面方法返回的都是uintptr类型,uintptr类型是个整数
alignof :查询字节对齐的大小,大部分是8字节,有些是4字节和1字节,有多种类型混合的话就按最大的字节对齐,
比如下面,字节对齐对大的是8字节 ,所以都按8字节来对齐
type a struct { n int32 s string p *int b bool up uintptr } a:=a{} log.println(unsafe.alignof(a.n)) //4 log.println(unsafe.alignof(a.s)) //8 log.println(unsafe.alignof(a.p)) // log.println(unsafe.alignof(a.b))//1 log.println(unsafe.alignof(a.up))//8
type s struct { i1 int32 //对齐大小是 4 i2 int32 // } s:=s{} log.println(unsafe.offsetof(s.i1))//0 log.println(unsafe.offsetof(s.i2))//4 这里就不是8开始了,因为都是4字节对齐
log.println(unsafe.offsetof(a.n)) //0 log.println(unsafe.offsetof(a.s)) //8 log.println(unsafe.offsetof(a.p)) //24 [+16 string的大小是16字节] log.println(unsafe.offsetof(a.b)) //32 log.println(unsafe.offsetof(a.up)) //40
下面展示指针堆结构体的操作,下面的操作会改变原来对象的值
type t struct { t1 byte t2 int32 t3 int64 t4 string t5 bool } //普通指针,用于传递对象地址,不能进行指针运算。 // //unsafe.pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算。 // //uintptr:用于指针运算,gc 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收。 func main() { t := &t{1, 2, 3, "this is a example", true} ptr := unsafe.pointer(t) t1 := (*byte)(ptr) log.println(*t1) //1 t2 := (*int32)(unsafe.pointer(uintptr(ptr) + unsafe.offsetof(t.t2))) *t2 = 99 log.println(t.t2) //99 被改变了 t3 := (*int64)(unsafe.pointer(uintptr(ptr) + unsafe.offsetof(t.t3))) *t3 = 123 log.println(t.t3) //123 }
下面看看go内置的数据类型slice
type slice struct { array unsafe.pointer //底层数组的指针 长度是8 len int //切片长度 int型size=8 cap int //切片实际长度 }
int main(){ s := make([]int, 9, 20) var len = *(*int)(unsafe.pointer(uintptr(unsafe.pointer(&s)) + uintptr(8))) log.println(len, len(s)) // 9 9 长度是9 var cap = *(*int)(unsafe.pointer(uintptr(unsafe.pointer(&s)) + uintptr(16))) //0+8+8 log.println(cap, cap(s)) // 20 20 cap是20 }
如对本文有疑问, 点击进行留言回复!!
网友评论