1.Golang指针基础概念
- *类型:普通指针类型,用于传递对象地址,不能进行指针运算。
- unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算,不能读取内存存储的值(必须转换到某一类型的普通指针)。
- uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收。
unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为 uintptr 进行指针运算。
unsafe.Pointer 不能参与指针运算,比如你要在某个指针地址上加上一个偏移量,Pointer是不能做这个运算的,那么谁可以呢?
就是uintptr类型了,只要将Pointer类型转换成uintptr类型,做完加减法后,转换成Pointer,通过*操作,取值,修改值,随意。
uintptr
// uintptr 是一个整数类型,它足够大,可以存储
type uintptr uintptr
unsafe.Pointer
type
ArbitraryType int
type
Pointer *ArbitraryType
func
Sizeof(x ArbitraryType) uintptr
func
Offsetof(x ArbitraryType) uintptr
func
Alignof(x ArbitraryType) uintptr
unsafe.pointer用于访问操作结构体的私有变量
package main
import (
"fmt"
"unsafe"
)
type user struct {
name string
age int
}
func main() {
u := new(user)
fmt.Println(*u)
pName := (*string)(unsafe.Pointer(u))
*pName = "张三"
pAge := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(u)) + unsafe.Offsetof(u.age)))
*pAge = 20
fmt.Println(*u)
}
获取 slice 长度
// runtime/slice.go
type slice struct {
array unsafe.Pointer // 元素指针
len int // 长度
cap int // 容量
}
func main() {
s := make([]int, 9, 20)
var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)))
fmt.Println(Len, len(s)) // 9 9
var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(16)))
fmt.Println(Cap, cap(s)) // 20 20
}
Len: &s => pointer => uintptr => pointer => *int => int
Cap: &s => pointer => uintptr => pointer => *int => int
Map结构
type hmap struct {
count int
flags uint8
B uint8
noverflow uint16
hash0 uint32
buckets unsafe.Pointer
oldbuckets unsafe.Pointer
nevacuate uintptr
extra *mapextra
}
和 slice 不同的是,makemap 函数返回的是 hmap 的指针,注意是指针:
func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap
我们依然能通过 unsafe.Pointer 和 uintptr 进行转换,得到 hamp 字段的值,只不过,现在 count 变成二级指针了:
&mp => pointer => **int => int