将数据结构转成 unsafe.Pointer
func Unsafe1() {
var m = map[int]int{
2: 10086,
3: 10087,
}
pointer := unsafe.Pointer(&m)
fmt.Println(*(*map[int]int)(pointer))
}
复制代码
Output:
map[2:10086 3:10087]
这里将m
转成unsafe.Pointer,之后我们手动将其转换((*map[int]int)(pointer))
)回来,之后解除引用取出里面的内容。
遇到 interface
空接口类型的特殊性
func Unsafe2() {
var m = map[int]int{
2: 10086,
3: 10087,
}
var i = func(v any) {//ps:笔者这里用1.18的版本,用了关键字`any`替换
pointer := unsafe.Pointer(&v)
fmt.Println(*(*map[int]int)(pointer))
}
i(&m)
}
复制代码
当你执行这段代码,并不会同 Unsafe1的代码一样正常,而是出现了异常报错信息 Output:
unexpected fault address 0x225d fatal error: fault [signal SIGSEGV: segmentation violation code=0x1 addr=0x225d pc=0x100d4c0]
goroutine 1 [running]: runtime.throw({0x10a1a5d?, 0x1741118?}) /Users/gre/Go/go1.18/src/runtime/panic.go:992 +0x71 fp=0xc000092a20 sp=0xc0000929f0 pc=0x102f411 runtime.sigpanic() ...
这个涉及了 interface
的类型转换逻辑 Interface接口设计 eface
简单来说就是在传递参数的过程中我们的结构进行了类型转换,所以我们没有办法直接转换回我们的数据。
那么我们应该怎么操作呢?
func Unsafe3() {
var m = map[int]int{
2: 10086,
3: 10087,
}
var i = func(v any) {
type eface struct {
rtype unsafe.Pointer
data unsafe.Pointer
}
pointer := (*eface)(unsafe.Pointer(&v))
fmt.Println(*(*map[int]int)(pointer.data))
}
i(&m)
}
复制代码
Output:
map[2:10086 3:10087]
我们定义了eface
类型来接收类型和数据类型,在取出data
转换我们需要的数据。
一般来说并不会让开发者去指定每一个数据的类型信息去解码,而是通过反射原本的结构体来实现。
func Unsafe4() {
var m = map[int]int{
2: 10086,
3: 10087,
}
var (
pointer = unsafe.Pointer(&m)
value1 = reflect.ValueOf(&m)
ptrRType = *(*unsafe.Pointer)(unsafe.Pointer(&value1))
)
var packTAny = func(rtype unsafe.Pointer, data unsafe.Pointer) any {
type eface struct {
rtype unsafe.Pointer
data unsafe.Pointer
}
var i interface{}
e := (*eface)(unsafe.Pointer(&i))
e.rtype = rtype
e.data = data
return i
}
face := packTAny(ptrRType, pointer)
m2 := (face).(*map[int]int)
fmt.Println(*m2)
}
复制代码
Output:
map[2:10086 3:10087]
runtime 运行时Go程序是如何解析结构体的
//go:noescape
//go:linkname mapiterinit reflect.mapiterinit
func mapiterinit(rtype unsafe.Pointer, m unsafe.Pointer, it *hiter)
//go:noescape
//go:linkname mapiternext reflect.mapiternext
func mapiternext(it *hiter)
func Unsafe5() {
var m = map[int]int{
2: 10086,
3: 10087,
}
var (
value = *(*unsafe.Pointer)(unsafe.Pointer(&m))
value1 = reflect.ValueOf(m)
ptrRType = *(*unsafe.Pointer)(unsafe.Pointer(&value1))
it hiter
)
mapiterinit(ptrRType, value, &it)
for {
key := it.key
elem := it.value
if key == nil {
return
}
fmt.Println(*((*int64)(key)))
fmt.Println(*((*int64)(elem)))
mapiternext(&it)
}
}
复制代码
Output:
map[2:10086 3:10087] 2 10086 3 10087
我们需要用 go:linkname
来关联到 runtime.map
的 mapiterinit
函数,在编译时能够引入该代码并执行。
你可以在这里学习 reflect 和 unsafe 的一些其它概念知识