【问题标题】:How to get the underlying array of a slice in Go?如何在 Go 中获取切片的底层数组?
【发布时间】:2016-08-10 22:48:27
【问题描述】:

假设我有以下长度为 3 的整数数组:

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

然后我只抓取前两项的切片

numSlice := nums[:2]

在 numSlice 和 nums 上调用 cap 在这两种情况下都会产生 3,len 会分别产生 2 和 3。

如果我随后附加到该切片 (numSlice = append(numSlice, 10)),则底层数组 (nums) 现在是 [1 2 10]cap 对于两者都保持在 3,因为切片的底层数组是相同的,切片的 len 现在是 3。

但是,如果我再次附加到该切片 (numSlice = append(numSlice, 20)),则切片的底层数组必须更改 - 我们看到 cap 现在 numSlice 翻倍并且 len 现在是 4 时就是这种情况。

很抱歉解释过度,只是让我自己通过它,但是有人可以向我解释底层数组的底层发生了什么以及如何获取对新数组的引用?

【问题讨论】:

    标签: arrays go slice


    【解决方案1】:

    首先,如果您还没有阅读过,请阅读this official blog post about slice internals。这应该可以解决所有问题。

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

    示例改编自 unsafe 包的文档:

    s := []int{1, 2, 3, 4}
    hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
    data := *(*[4]int)(unsafe.Pointer(hdr.Data))
    

    【讨论】:

    • 值得注意的是,Data 可能不指向支持数组的开头:只是指向切片的第 0 个元素。因此,在numSlice := nums[1:] 的情况下,它实际上最终会指向数组的第二个元素。 Example
    • 作为一个可能很愚蠢的问题,这方面的表现如何?
    【解决方案2】:

    作为提醒,回答您的第二个问题。从Go 1.17开始,可以这样操作

    (*[2]int)(numSlice)
    

    Playground

    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),即使它当前被3 占用,也可以使用它,并且310 覆盖。

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

    【讨论】:

      猜你喜欢
      • 2021-07-31
      • 2022-01-25
      • 2019-12-16
      • 2015-12-29
      • 2018-02-15
      • 2016-06-24
      • 2012-11-10
      • 2021-04-29
      • 1970-01-01
      相关资源
      最近更新 更多