1. 首页
  2. 主题
  3. Go语言

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

Lambda0828 · · 1958 次点击
**获取 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)取值就可以获取数量了。
抄袭就算了,连`qcrao`都懒得改。 以饶全成的量级,现在绝对不会再写这种helloworld级别的文章了。
#1

用户登录

没有账号?注册

今日阅读排行

    加载中

一周阅读排行

    加载中