unsafe包
- unsafe包中的操作是不安全的操作,会绕过go原因的内存保护机制
- 引用有风险,使用须谨慎
- unsafe包提供了直接操作内存的能力
unsafe源码
- unsafe包中只包含两个结构体和三个函数
|
|
两个变量
|
|
- ArbitraryType实际上并不是unsafe包的一部分,只是为了文档展示
- ArbitraryType虽然类型时int,但是其实质上代表的是任意类型的go表达式
- - Pointer变量代表一个指向ArbitraryType对象的指针,实际上就是一个指向任意类型的指针
- Pointer结构体有四个其他类型的结构体不具有的操作,这些能力使得go可以无视类型的限制来读写任意的内存,使用的时候需要谨慎
- 一个任意结构体类型的指针可以转换为Pointer类型
- 一个Pointer类型的结构体可以转换成一个指针
- 一个uintptr可以转换成Pointer类型
- 一个Pointer类型的结构体可以转换成一个uintptr指针,uintptr是一个能存储指针的整形值
|
|
- unsafe包规定了一些Pointer合理的使用模式,不符合的可能已经不可用或者即将不可用
- 将*T1类型的指针转换成Pointer类型,然后再讲Pointer类型转换成T2类型指针
- 这要求T1占用的内存要大于T2占用的内存
- 如math.Float64bits的实现
- 将一个Pointer类型转换成一个uintptr类型,也就是将指向的值的地址作为一个uintptr变量的值,
- 将uintptr转换成Pointer通常来说会产生问题
- uintptr是个值,不是指针,因此其没有任何语义
- uintptr即使有某个对象的地址,但是在gc的时候是不会影响该对象的gc操作的
- Pointer -> uintptr -> Pointer有时候是合法的,如进行算数运算的
p = unsafe.Pointer(uintptr(p)+offset)
- 最常用的地方是获取结构体中的元素指针或者数组中的元素
f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f))
等价于f := unsafe.Pointer(&s.f)
e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0]))
等价于e := unsafe.Pointer(&x[i])
- 但是如果计算完指向了未分配的内存,则是不合法的
- 调用syscall.Syscall的时候将Pointer类型转换成uintptr
- 将reflect.Value.Pointer或者reflect.Value.UnsafeAddr的结果从uintptr转换成Pointer类型时合法的
- 这是因为reflect返回uintptr可以让上层调用不必引入unsafe包
p :+ (*int)(unsafe.Pointer(reflect.ValueOf(new)))
- Pointer可以与reflect.SliceHeader或者reflect.StringHeader的Data字段进行互相转换
hdr.Data = uintptr(unsafe.Pointer(p))
- 将*T1类型的指针转换成Pointer类型,然后再讲Pointer类型转换成T2类型指针
|
|
三个函数
|
|
- 三个函数传入的参数为ArbitraryType类型变量,也就是可以穿任意类型的数据的地址进来
- SizeOf函数返回x类型占据的字节数的
|
|
- Offsetof实际上返回的是当前结构体开始的位置到当前字段的位置之间的偏移量
- Alignof返回变量对其字节数量ß