暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Go语言之unsafe

欢乐毅城 2021-07-28
483

注:每天进步一点点,记录每日成长,思考的第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进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论