如何利用 unsafe 获取 slice 和 map 的长度
通过前面关于 slice 的章节内容介绍,知道了 slice header 的结构体定义:
// runtime/slice.go
type slice struct {
array unsafe.Pointer // 元素指针
len int // 长度
cap int // 容量
}
1
2
3
4
5
6
7
2
3
4
5
6
7
使用 make 新建一个 slice,底层调用的是 makeslice 函数,返回的是 slice 结构体指针:
func makeslice(et *_type, len, cap int) unsafe.Pointer
1
因此可以通过 unsafe.Pointer 和 uintptr 进行转换,进而得到 slice 的长度和容量:
package main
import (
"fmt"
"unsafe"
)
func main() {
s := make([]int, 9, 20)
var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)))
var l = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(int(0))))
fmt.Println(Len, len(s), l)
var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(16)))
var c = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(int(0)) + unsafe.Sizeof(int(0))))
fmt.Println(Cap, cap(s), c)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Len 和 Cap 的转换流程如下:
Len: &s => pointer => uintptr => pointer => *int => int
Cap: &s => pointer => uintptr => pointer => *int => int
1
2
2
再来看一下前面章节讲到的 map:
// src/runtime/map.go
type hmap struct {
count int
flags uint8
B uint8
noverflow uint16
hash0 uint32
buckets unsafe.Pointer
oldbuckets unsafe.Pointer
nevacuate uintptr
extra *mapextra
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
和 slice 不同的是,makemap 函数返回的是 hmap 的指针,注意是指针:
func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap
1
依然能通过 unsafe.Pointer 和 uintptr 进行转换,得到 hamp 字段的值,只不过,现在 count 变成二级指针了:
func main() {
mp := make(map[string]int)
mp["qcrao"] = 100
mp["stefno"] = 18
count := **(**int)(unsafe.Pointer(&mp))
fmt.Println(count, len(mp)) // 2 2
}
1
2
3
4
5
6
7
2
3
4
5
6
7
所以 count 的转换过程如下:
&mp => pointer => **int => int
1
上次更新: 7/12/2024, 2:37:05 PM