unsafe.Pointer本质是一个int的指针
// src\unsafe\unsafe.go
type ArbitraryType int
type Pointer *ArbitraryType
不同于其他类型,unsafe.Pointer支持4种特殊操作
uintptr是一个整数类型,并且足够大来表示任意地址
// src\builtin\builtin.go
// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr
这种类型转换,需要保证T2的size不大于T1,且具有相同的内存布局;否则,当对T2的读写超过T1的size时,可能会发生错误
// go version go1.14 windows/amd64
package main
import (
"fmt"
"unsafe"
)
type UINT64 struct {
low uint32
high uint32
}
func main() {
data := UINT64{low: 0xf, high: 0xa}
ptr := unsafe.Pointer(&data) // *UINT64 => unsafe.Pointer
data2 := *(*uint64)(ptr) // unsafe.Pointer => *uint64
fmt.Printf("%x\n", data2) // 0xa0000000f
var data32 uint32 = 0xf
ptr = unsafe.Pointer(&data32) // uint32 => unsafe.Pointer
data64 := *(*uint64)(ptr) // unsafe.Pointer => *uint64
data64 |= (0xa << 32) // 修改高32位值
fmt.Printf("%x,%x\n", data64, 0xf|0xa00000000) // 0x4adfea0000000f,0xa0000000f
}
unsafe.pointer是普通指针类型,不能直接进行指针运算;需要先转换成uintptr,运算之后再转换成unsafe.pointer
package main
import (
"fmt"
"unsafe"
)
type UINT64 struct {
low uint32
high uint32
}
func main() {
data := UINT64{low: 0xf, high: 0xa}
dataAddr := unsafe.Pointer(&data) // *UINT64 => unsafe.Pointer
dataPtr := uintptr(dataAddr) // unsafe.Pointer => uintptr
highAddr := unsafe.Pointer(dataPtr + unsafe.Offsetof(data.high)) // 计算data中high字段地址
*(*uint32)(highAddr) = 0xb // 修改high字段地址存储的值
data2 := *(*uint64)(dataAddr) // unsafe.Pointer => *uint64
fmt.Printf("%x\n", data2) // 0xb0000000f
}
uintptr在转换成unsafe.Pointer之前,不能保存在临时变量中;因为uintptr只是一个内存地址的值,当变量因为GC或者其他原因导致地址变动时,uintptr并不会同步修改
package main
import (
"fmt"
"time"
"unsafe"
)
func main() {
var ptr uintptr
go func(ptr *uintptr) {
var data uint64 = 5
*ptr = uintptr(unsafe.Pointer(&data)) // 获取data的地址
fmt.Println(*ptr, *(*uint64)(unsafe.Pointer(*ptr))) // 824634023832 5
}(&ptr)
time.Sleep(1 * time.Second) // 保证data被回收
fmt.Println(ptr, *(*uint64)(unsafe.Pointer(ptr))) // 824634023832 4437453
}
KGo笔记
全部评论