为了账号安全,请及时绑定邮箱和手机立即绑定

如何在 Go 中获取切片的底层数组?

如何在 Go 中获取切片的底层数组?

Go
泛舟湖上清波郎朗 2022-01-04 19:33:56
假设我有以下长度为 3 的整数数组:nums := [3]int{1,2,3}然后我抓住前两个项目的切片numSlice := nums[:2]cap在这两种情况下调用numSlice 和 nums 都会产生 3,并且len分别产生 2 和 3。如果我随后附加到该切片 ( numSlice = append(numSlice, 10)),则底层数组 ( nums) 现在是[1 2 10]. cap两者都保持为 3,因为切片的底层数组是相同的,切片的 len 现在为 3。但是,如果我再次附加到该切片 ( numSlice = append(numSlice, 20)),则该切片的底层数组必须更改 - 我们看到这是cap现在 numSlice 的两倍并且 len 现在是 4 的情况。对不起,解释过度了,只是自己走过它,但有人可以向我解释底层数组的幕后情况以及如何获取对新数组的引用吗?
查看完整描述

2 回答

?
慕田峪4524236

TA贡献1875条经验 获得超5个赞

首先,如果你还没有,你应该阅读这篇关于 slice internals 的官方博客文章。那应该清除一切。


现在访问的底层数组,你可以使用的组合reflect和unsafe。特别是,reflect.SliceHeader 包含一个Data字段,该字段包含一个指向切片底层数组的指针。


改编自unsafe包文档的示例:


s := []int{1, 2, 3, 4}

hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))

data := *(*[4]int)(unsafe.Pointer(hdr.Data))


查看完整回答
反对 回复 2022-01-04
?
阿晨1998

TA贡献2037条经验 获得超6个赞

就像一个提示,回答你的第二个问题。从Go 1.17开始,你可以这样做


(*[2]int)(numSlice)



package main


import (

    "fmt"

)


func main() {

    nums := [3]int{1, 2, 3}

    numSlice := nums[:2]

    underArr1 := (*[2]int)(numSlice)

    fmt.Println(&underArr1[0]) //0xc000016018


    numSlice = append(numSlice, 10)

    underArr2 := (*[3]int)(numSlice)

    fmt.Println(&underArr2[0]) //0xc000016018 - same

    fmt.Println(nums) // [1 2 10]


    numSlice = append(numSlice, 20)

    underArr3 := (*[3]int)(numSlice)

    fmt.Println(&underArr3[0]) //0xc000078030 - different

    fmt.Println(cap(numSlice)) // 6

}

老实说,你不必转换为数组指针来查看地址,我只是为了回答你的第二个问题。


该行为确实是您描述的方式。当您追加 时10,您的底层数组中仍然剩下一个字段(因为它的长度是 3,但是您的 numSlice 是 2),即使它当前被 a 占用3,它也可以使用,并被3覆盖10。


当您附加 a 时20,没有剩余的字段,因此它会创建一个新的底层数组(很可能有 6 个字段长,两倍大)并将原始数组中的所有数据复制到那里,并将指针移动到该数组。


查看完整回答
反对 回复 2022-01-04
  • 2 回答
  • 0 关注
  • 424 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信