unsafe

97 阅读3分钟

顾名思义,unsafe 包是不安全的,为了安全起见我们尽量不要使用,不过 unsafe 也有优势便是可以绕过 go 的内存安全机制,直接对内存进行读写。所以有时候出于性能需要,还是会冒险使用它来对内存进行操作。接下来我将会介绍一系列 unsafe 包的应用。

指针类型转换

go 是一门强类型的静态语言,强类型意味着一旦定义了,类型就不能改变;静态意味着类型检查在运行前就做了。同时,go 是不允许两个指针类型进行转换的,比如 *int 不能转换为 *float64 。

package main

func main() {
   v1 := 1
   p1 := &v1
   var p2 *float64 = (*float64)(p1)
}

运行这段代码,报错:cannot convert p1 (variable of type *int) to type *float64 ,这时我们便可以使用 unsafe 包里的 Pointer 进行转换。

unsafe.Pointer

unsafe.Pointer 可以表示任意类型的指针,类似于 c 语言中的 void* 。

正常情况下,*int 不能转换为 *float64 ,但如果我们先用 unsafe.Pointer 先转换一次就可以了。

func main() {
   v1 := 1
   p1 := &v1
   var p2 *float64 = (*float64)(unsafe.Pointer(p1))
   *p2 *= 2
   fmt.Println(v1)
}

上面的代码是可以正常运行的,并且我们对 p2 操作,最终打印结果 v1 也发生了改变。

通过这个例子,我们可以知道,只需要借助 unsafe.Pointer 我们便可以实现任意指针类型间的相互转换,但有一点仍需知道,unsafe.Pointer 是不支持运算的,如果我们想要直接对指针进行运行,我们便需要使用到 unitptr 。

uintptr

() uintptr 也是一种指针类型,它也可以表示任意指针。

// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr

下面的是使用指针偏移运算来实现修改结构体字段的例子:

type person struct {
   Name string
   Age  int
}

func main() {
   p := new(person)
   //Name是person的第一个字段不用偏移,即可通过指针修改
   pName := (*string)(unsafe.Pointer(p))
   *pName = "tom"
   //Age并不是person的第一个字段,所以需要进行偏移,这样才能正确定位到Age字段这块内存,才可以正确的修改
   pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + unsafe.Offsetof(p.Age)))
   *pAge = 20
   fmt.Println(*p)
}

需要注意的是,如果要进行指针运算,要先通过 unsafe.Pointer 转换为 uintptr 类型的指针。指针运算完毕后,还要通过 unsafe.Pointer 转换为真实的指针类型(比如示例中的 *int 类型),这样可以对这块内存进行赋值或取值操作。

指针运算的核心在于它操作的是一个个内存地址,通过内存地址的增减,就可以指向一块块不同的内存并对其进行操作,而且不必知道这块内存被起了什么名字(变量名)。

unsage.Sizeof

Sizeof 返回一个类型所占内存的大小,与 c 语言的 sizeOf 相似

ps:一个 struct 结构体的内存占用大小,等于它包含的字段类型内存占用大小之和。

unsafe.Alignof

Alignof 返回一个类型的对齐值,也可以叫做对齐系数或者对齐倍数。对齐值是一个和内存对齐有关的值,合理的内存对齐可以提高内存读写的性能。

unsafe.Alignof(x)等价于reflect.TypeOf(x).Align()。

总结

使用 unsafe 包可以让你在 *T、uintptr 及 Pointer 三者间转换,达到例如零内存拷贝或通过 uintptr 进行指针运算的目的。

但是 unsafe 包会避开 go 语言编译器的检查,如果操作不熟练就有可能出现内存方面的问题。但有些情况还是建议使用,比如 []byte 与 string 的相互转换便是通过 unsafe.Pointer 实现。