使用unsafe.Pointer获取slice和map的长度

Lambda0828 · · 1563 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

**获取 slice 长度 ** 通过对slice的学习,我们知道了 slice header 的结构体定义: ```go // runtime/slice.go type slice struct { array unsafe.Pointer // 元素指针 len int // 长度 cap int // 容量 } ``` 调用 make 函数新建一个 slice,底层调用的是 makeslice 函数,返回的是 slice 结构体: ``` func makeslice(et *_type, len, cap int) slice``` 因此我们可以通过 unsafe.Pointer 和 uintptr 进行转换,得到 slice 的字段值。 ```go 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 } ``` 上文采用加uintptr(8)和uintptr(16),是因为底层slice结构连续的,切片变量 s 在内存中占用了连续的 3 个机器字。在 64 位架构下,一个机器字的大小为 8 个字节,因此 s 的第一个机器字是指向底层数组的指针,第二个机器字是切片的长度,第三个机器字是切片的容量。可能有些小伙伴不了解字的含义,这里和大家解释一下: ``` 16位系统: 1个机器字---2个字节--16位bit 32位系统: 1个机器字---4个字节--32位bit 64位系统: 1个及其子---8个字节--64位bit 其中:字节是内存操作的基本单位,所以uintptr(8)才能计算偏移位置。 ``` slice内存结构如下: ```go +--------------+ | Ptr | -- 指向底层数组的指针 +--------------+ | Len | -- 切片的长度 +--------------+ | Cap | -- 切片的容量 +--------------+ ``` Len,cap 的转换流程如下: Len: &s => pointer => uintptr => pointer => *int => int Cap: &s => pointer => uintptr => pointer => *int => int **获取 map 长度 ** 再来看一下 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 } ``` 和 slice 不同的是,makemap 函数返回的是 hmap 的指针,注意是指针: ``` func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap``` 我们依然能通过 unsafe.Pointer 和 uintptr 进行转换,得到 hmap 字段的值,只不过,现在 count 变成二级指针了: ```go 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 } ``` count 的转换过程: &mp => pointer => **int => int 注意:上面的转化,是因为hmap第一个成员是count,所以可以将*hmap指针转化为*int指针。转化后获取的就是count地址,然后进行(**int)取值就可以获取数量了。

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1563 次点击  
加入收藏 微博
1 回复  |  直到 2023-04-22 11:29:46
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传