个人笔记记录
是什么unsafe
Package unsafe contains operations that step around the type safety of Go programs.
为什么会有unsafe
go的指针是类型安全的,所以有时我们需要绕过这个限制来达到实现某个功能的目的
unsafe提供的方法
type ArbitraryType int
type Pointer *ArbitraryType
ArbitraryType表示一个任意的Go表达式的类型,所以可以把Pointer当作C语言里的void*
以下是unsafe包提供的三个函数
func Sizeof(x ArbitraryType) uintptr
func Offsetof(x ArbitraryType) uintptr
func Alignof(x ArbitraryType) uintptr
上面三个函数都在编译期间运行,所以都可以复制给const常量
package main
import (
"fmt"
"unsafe"
)
type Num struct {
i string
j int
}
// invalid: const initializer unsafe.Pointer(&Num{}) is not a constant
// const pointer = unsafe.Pointer(&Num{})
const sizeOf = unsafe.Sizeof(Num{})
const offsetOf = unsafe.Offsetof(Num{}.i)
const alignOf = unsafe.Alignof(Num{})
func main() {
fmt.Println(sizeOf)
fmt.Println(offsetOf)
fmt.Println(alignOf)
}
// 24
// 0
// 8
unsafe的pointer只支持如下四种操作,我们都是根据这四种操作来实现我们的需求的
A pointer value of any type can be converted to a Pointer.
A Pointer can be converted to a pointer value of any type.
A uintptr can be converted to a Pointer.
A Pointer can be converted to a uintptr.
Pointer不能进行地址偏移运算,需要转成uintptr,进行运算后再转回Pointer,并且uintptr仅仅是一个整数,和指针以及指向的对象没任何关系,所以不要把uintptr保存下来留到以后使用,因为对象可能被gc移动或回收,但是uintptr的值并不会被自动更新。而unsafe.Pointer 有指针语义,可以保护它所指向的对象在“有用”的时候不会被垃圾回收.
unsafe使用
获取StringHeader的Len值
StringHeader结构如下
type StringHeader struct {
Data uintptr
Len int //通过unsafe.Offsetof拿到Len的偏移为8
}
s := "12345"
fmt.Println((*reflect.StringHeader)(unsafe.Pointer(&s)).Len) // 5
fmt.Println(*(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)))) // 5
获取slice信息
sl := make([]int, 5, 10)
slLen := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&sl)) + uintptr(8)))
slCap := *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&sl)) + uintptr(16)))
fmt.Println("slLen: ", slLen) // 5
fmt.Println("slCap: ", slCap) // 10
使用offset
package main
import (
"fmt"
"unsafe"
)
type Person struct {
name string
age int
}
func main() {
p := Person{}
fmt.Println(p) // { 0}
namePtr := (*string)(unsafe.Pointer(&p))
*namePtr = "a"
agePtr := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&p)) + unsafe.Offsetof(p.age)))
*agePtr = 100
fmt.Println(p) // {a 100}
}
string 和 slice 的相互转换
type StringHeader struct {
Data uintptr
Len int
}
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
sli := []byte{'a', 'b', 'c', '1', '2', '3', '4'}
fmt.Println(slice2string(sli)) // abc1234
str := "qwert"
fmt.Println(string2slice(str)) // [113 119 101 114 116]
}
func slice2string(b []byte) string {
sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
strH := reflect.StringHeader{
Data: sliceHeader.Data,
Len: sliceHeader.Len,
}
return *(*string)(unsafe.Pointer(&strH))
}
func string2slice(str string) []byte {
strHeader := (*reflect.StringHeader)(unsafe.Pointer(&str))
sliH := reflect.SliceHeader{
Data: strHeader.Data,
Len: strHeader.Len,
Cap: strHeader.Len,
}
return *(*[]byte)(unsafe.Pointer(&sliH))
}
修改未导出字段
├── main.go
└── testpa
└── testpa.go
test.go
package test
type Test struct {
name string
age int
}
var T = Test{
name: "old",
age: 0,
}
main.go
package main
import (
"fmt"
"unsafe"
"example/testpa"
)
func main() {
val := testpa.T
fmt.Println(val) // {old 0}
*(*string)(unsafe.Pointer(uintptr(unsafe.Pointer(&val)))) = "new"
*(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&val)) + uintptr(16))) = 10
fmt.Println(val) // {new 10}
}