利用unsafe修改私有变量
首先介绍一下unsafe.Pointer,它与普通的指针区别在于其可与其他类型的指针、uintptr互换。uintptr表示地址的整型,转化为其他类型就可以修改变量值,转化为uintptr就可以参与四则运算,定位变量地址
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
}
利用sizeof也可以做到,不在演示
获得slice、map长度
先看两者的结构体定义,以便定位len、cap字段
type hmap struct {
count int
flags uint8
B uint8
noverflow uint16
hash0 uint32
buckets unsafe.Pointer
oldbuckets unsafe.Pointer
nevacuate uintptr
extra *mapextra
}
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
mp := make(map[string]int)
mp["qcrao"] = 100
mp["stefno"] = 18
count := **(**int)(unsafe.Pointer(&mp))
fmt.Println(count, len(mp)) // 2 2
}
上述map和slice的取地址操作不同,原因在于两者使用make创建的返回值不同,makemap返回*map,makeslice返回slice结构体,前者需要解引用两次,后者只需要一次
实现string和byte[]互转且zero-copy
观察两者结构体
type StringHeader struct {
Data uintptr
Len int
}
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
两者Data和Len字段类型相同,直接转化就行