获取 slice 长度

通过前面关于 slice 的文章,我们知道了 slice header 的结构体定义:

  1. // runtime/slice.go
  2. type slice struct {
  3. array unsafe.Pointer // 元素指针
  4. len int // 长度
  5. cap int // 容量
  6. }

调用 make 函数新建一个 slice,底层调用的是 makeslice 函数,返回的是 slice 结构体:

  1. func makeslice(et *_type, len, cap int) slice

因此我们可以通过 unsafe.Pointer 和 uintptr 进行转换,得到 slice 的字段值。

  1. func main() {
  2. s := make([]int, 9, 20)
  3. var Len = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(8)))
  4. fmt.Println(Len, len(s)) // 9 9
  5. var Cap = *(*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + uintptr(16)))
  6. fmt.Println(Cap, cap(s)) // 20 20
  7. }

Len,cap 的转换流程如下:

  1. Len: &s => pointer => uintptr => pointer => *int => int
  2. Cap: &s => pointer => uintptr => pointer => *int => int

获取 map 长度

再来看一下上篇文章我们讲到的 map:

  1. type hmap struct {
  2. count int
  3. flags uint8
  4. B uint8
  5. noverflow uint16
  6. hash0 uint32
  7. buckets unsafe.Pointer
  8. oldbuckets unsafe.Pointer
  9. nevacuate uintptr
  10. extra *mapextra
  11. }

和 slice 不同的是,makemap 函数返回的是 hmap 的指针,注意是指针:

  1. func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap

我们依然能通过 unsafe.Pointer 和 uintptr 进行转换,得到 hamp 字段的值,只不过,现在 count 变成二级指针了:

  1. func main() {
  2. mp := make(map[string]int)
  3. mp["qcrao"] = 100
  4. mp["stefno"] = 18
  5. count := **(**int)(unsafe.Pointer(&mp))
  6. fmt.Println(count, len(mp)) // 2 2
  7. }

count 的转换过程:

  1. &mp => pointer => **int => int