分享
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。
**获取 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
关注微信1975 次点击
添加一条新回复
(您需要 后才能回复 没有账号 ?)
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传