注:每天进步一点点,记录每日成长,思考的第08/365天 ;
本文预计阅读时间: 4分钟
1
01
unsafe 的定义
顾名思义,unsafe是不安全的,那导致哪里不安全了?unsafe的不安全就在于,它可以绕开Go的类型系统,直接操作内存。
1
02
源码分析
package unsafe
//ArbitraryType仅用于文档目的,实际上并不是unsafe包的一部分,它表示任意Go表达式的类型。
type ArbitraryType int
//任意类型的指针,类似于C的*void
type Pointer *ArbitraryType
//确定结构在内存中占用的字节数(并不是内容大小)
func Sizeof(x ArbitraryType) uintptr
//返回结构体中某个field的偏移量(离结构体起始位置的字节数)
func Offsetof(x ArbitraryType) uintptr
//返回结构体中某个field的对齐值(也叫对齐系数或对齐倍数,一般为2^n,最大不超过8)
//unsafe.Alignof(x)至少为1;结构体变量x的对齐值,等于字段对齐值的最大值
//对齐值也可以用反射包中函数reflect.TypeOf(x).Align()获取
func Alignof(x ArbitraryType) uintptr
Go语言中有3种指针类型:
*T: 普通类型指针类型,用于传递地址,不能进行指针的运算;
unsafe.Pointer : 通用指针类型,用于转换不同类型指针(中介),不能读取内存中存储的值,也不能进行指针运算;
uintptr:可用于指针运算,GC
不把uintptr
当指针,uintptr
无法持有对象。uintptr
类型的目标会被回收。
关于指针操作,在unsafe包官方定义里有四个描述:
1)任何类型的指针都可以被转化为Pointer;
2)Pointer可以被转化为任何类型的指针;
3)uintptr可以被转化为Pointer;
4)Pointer可以被转化为uintptr。
指向不同类型数据的指针,是无法直接相互转换的,必须借助unsafe.Pointer。
1
03
unsafe基本使用
案例1:
var stu struct {
a bool
b string
c int
d []int
}
//stu的地址再加c的偏移量得到的c的地址pb
pb := (*int16)(unsafe.Pointer(uintptr(unsafe.Pointer(&stu)) + unsafe.Offsetof(stu.c)))
fmt.Printf("%v %v\n", pb, &stu.c) //0xc00003c058 0xc00003c058
*pb = 99
fmt.Println(stu.c) // 99
但是 uintptr 传递的不都是合法的内存地址,这样做会破坏类型系统。
案例2:(错误示范)
不要试图引入一个uintptr类型的临时变量,因为它可能会破坏代码的安全性。
tmp := uintptr(unsafe.Pointer(&stu)) + unsafe.Offsetof(stu.c)
pb := (*int16)(unsafe.Pointer(tmp))
*pb = 1
因为 uintptr 不是真的指针(一个普通的数字 ),如此时刚好进行GC(垃圾回收器会移动一些变量以降低内存碎片等问题 ),这时候的地址可能已经不是之前那个地址了。
当进行垃圾回收时变量移动,指针(unsafe.Pointer、*T)的值也跟随这变量的地址改变而改变,但是上面例子 tmp 是一个 uintptr 类型变量,GC 不会进行指针同步,缓存失效。
文章转载自欢乐毅城,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




