使用golang-unsafe包的注意事项

本文详细介绍了在Golang中使用unsafe包时的注意事项,包括:禁止使用ArbitraryType,Pointer类型转换的限制,uintptr转换的安全条件,以及在syscall、反射包中的特殊转换。强调所有转换应在同一表达式内完成,以保证Pointer的有效性。
摘要由CSDN通过智能技术生成

基于golang 15.5

总结(详细的内容可以往下看):

1.不能使用unsafe包里的ArbitraryType类型
2.Pointer类型可以表示任意类型的指针,所以可以用Pointer类型作为中介进行两种不同类型指针的转换,为保证作为中介的Pointer类型数据有效,必须保证所有转换在同一个表达式中,如:

func Float64bits(f float64) uint64 {
   
	return *(*uint64)(unsafe.Pointer(&f))
}

3.Pointer类型可以安全有效的转换成uintptr,不能以任何形式(包含变量)保存转换后的uintptr值,转换后的uintptr值仅在转换所在的表达式中有效的。
4.因为uintptr只是内存地址值,并没有指针语义,所以uintptr转换成Pointer通常不会是有效的,下面列举有效的转换方式:

  • 1:在同一个表达式内,对Pointer转换成的uintptr值进行算术运算(包括加减偏移量等,和c不同,指向初始分配内存的end边界点是无效的),然后在转换回Pointer
  • 2:使用syscall.Syscall. syscall包的Syscall函数直接将uintptr传递给操作系统,根据调用的细节,会将他们中的一些重新转换为指针,系统调用将会隐式转换uintptr成pointer,如果一个指针作为函数的实参,而对应的形参是uintptr,那这个转换必须写在调用函数的表达式上,如
syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n))
  • 3:反射包的Value类型的方法Pointer()和UnsafeAddr()返回uintptr而不是unsafe.Pointer,以防止在没有导入unsafe包情况下,调用者将结果更改为任意类型。但是这意味着结果是不稳定的,必须在调用后立即在同一表达式中将其转换为Pointer,如:
p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer()))
  • 4:反射包的结构体SliceHeader和StringHeader将字段Data声明为uintptr,以防止调用者在不没有导入unsafe包的情况下将结果更改为任意类型。 但是,这意味着SliceHeader和StringHeader仅在解释实际切片或字符串值的内容时才有效。如:
var s string
hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1
hdr.Data = uintptr(unsafe.Pointer(p))              // case 6 (this case)
hdr.Len = n

详细内容:

1.ArbitraryType类型在本文档里表示任意一种类型,但并非一个实际存在于unsafe包的类型。

2.Pointer类型用于表示任意类型的指针。有4个特殊的只能用于Pointer类型的操作:

  1. 任意类型的指针可以转换为一个Pointer类型值
  2. 一个Pointer类型值可以转换为任意类型的指针
  3. 一个uintptr类型值可以转换为一个Pointer类型值
  4. 一个Pointer类型值可以转换为一个uintptr类型值

因此,Pointer类型允许程序绕过类型系统读写任意内存。使用它时必须谨慎。

3.以下使用Pointer类型的示范操作都能确保所使用的Pointer是有效的,不遵循这些示范的操作在后续的golang版本的迭代中不保证操作包含的Pointer是有效的

  1. Conversion of a *T1 to Pointer to *T2,Sizeof(T2)必须小于等于Sizeof(T1),下面是例子
func Float64bits(f float64) uint64 {
   
	return *(*uint64)(unsafe.Pointer(&f))
}
  1. Conversion of a Pointer to a uintptr (but not back to Pointer),将Pointer转换成uintptr会用Pointer产生一个不带指针语义的整数值并赋值给uintptr,这个整数值是一个内存地址值,尽管是内存地址值,但是当这个内存地址对应的对象被移动至其他内存区域或者对应的对象被回收时,gc并不会更新这个值,所以将一个uintptr转换成Pointer通常不会是有效的,下面列举了将uintptr转换成Pointer有效的几种方式

  2. Conversion of a Pointer to a uintptr and back, with arithmetic.这种方式通常用于获取结构体字段值或者数组元素值,下面是例子

p = unsafe.Pointer(uintptr(p) + offset)

// equivalent to f := unsafe.Pointer(&s.f)
f := unsafe.Pointer(uintptr
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值