Ezreal 书架 Ezreal 书架
Home
  • 《Go程序员面试笔试宝典》
  • 《RabbitMQ 实战指南》
  • 《深入理解kafka》
  • MySQL45讲
  • 透视HTTP协议
  • 结构化数据的分布式存储系统
  • Raft 共识算法
Home
  • 《Go程序员面试笔试宝典》
  • 《RabbitMQ 实战指南》
  • 《深入理解kafka》
  • MySQL45讲
  • 透视HTTP协议
  • 结构化数据的分布式存储系统
  • Raft 共识算法
  • 逃逸分析

  • 延迟语句

  • 数据容器

  • 通道

  • 接口

  • unsafe

    • 如何利用 unsafe 包修改私有成员
    • 如何利用 unsafe 获取 slice 和 map 的长度
    • 如何实现字符串和 byte 切片的零复制转换
  • context

  • Go程序员面试笔试宝典
  • unsafe
ezreal_rao
2023-06-01

如何利用 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

使用 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

Len 和 Cap 的转换流程如下:

Len: &s => pointer => uintptr => pointer => *int => int
Cap: &s => pointer => uintptr => pointer => *int => int
1
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

和 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

所以 count 的转换过程如下:

&mp => pointer => **int => int
1
#golang#unsafe
上次更新: 7/12/2024, 2:37:05 PM
如何利用 unsafe 包修改私有成员
如何实现字符串和 byte 切片的零复制转换

← 如何利用 unsafe 包修改私有成员 如何实现字符串和 byte 切片的零复制转换→

最近更新
01
为什么我的MySQL会抖一下
07-15
02
HTTP 性能优化面面观
07-12
03
WebSocket:沙盒里的 TCP
07-12
更多文章>
Theme by Vdoing | Copyright © 2022-2024 Ezreal Rao | CC BY-NC-SA 4.0
豫ICP备2023001810号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式