【问题标题】:Understanding Go's slice notation理解 Go 的切片符号
【发布时间】:2018-07-28 22:08:26
【问题描述】:

我正在通过 Go-tour 和 this 模块让我思考,似乎每次通过切片下限来修改视图时,切片的容量和长度都会减少。然而,如前所述,切片创建的底层数组不会改变。

我将示例代码简化为:

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}

    s = s[6:]
    fmt.Println(cap(s), len(s), s)
}

这会打印:0 0 [],这意味着这个切片现在完全没用了。

但是,数组没有改变,数组仍然(为了可视化):

[2, 3, 5, 7, 11, 13]

并由s 引用,这意味着它不会被垃圾收集。

所以我的问题是,这是切片的副作用还是这是预期/首选的行为?其次,有没有办法将视图恢复到原来的样子? (显示[2, 3, 5, 7, 11, 13]

【问题讨论】:

    标签: arrays go garbage-collection slice


    【解决方案1】:

    关于你的第二个问题,是的,你可以恢复原状,但只能使用非常黑暗的反射魔法和不安全。

    hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s)) //extract SliceHeader
    arr := *(*[6]int)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&s)).Data)) //restore array data
    

    playground

    它不能安全或便携地使用,它的表示可能会改变 在以后的版本中。

    不要在生产中使用它。

    【讨论】:

      【解决方案2】:

      您似乎了解 Go 切片。


      Go 切片被实现为 struct:

      type slice struct {
          array unsafe.Pointer
          len   int
          cap   int
      }
      

      它是底层数组的视图。

      例如,

      package main
      
      import "fmt"
      
      func main() {
          s := []int{2, 3, 5, 7, 11, 13}
          fmt.Println("s", cap(s), len(s), s)
      
          t := s[cap(s):]
          fmt.Println("s", cap(s), len(s), s)
          fmt.Println("t", cap(t), len(t), t)
      
          t = s
          fmt.Println("s", cap(s), len(s), s)
          fmt.Println("t", cap(t), len(t), t)
      }
      

      游乐场:https://play.golang.org/p/i-gufiJB-sP

      输出:

      s 6 6 [2 3 5 7 11 13]
      s 6 6 [2 3 5 7 11 13]
      t 0 0 []
      s 6 6 [2 3 5 7 11 13]
      t 6 6 [2 3 5 7 11 13]
      

      在没有对底层数组任何元素的引用(指针)之前,不会对底层数组进行垃圾回收。

      例如,

      package main
      
      import "fmt"
      
      func main() {
          s := []int{2, 3, 5, 7, 11, 13}
          fmt.Println("s", cap(s), len(s), s, &s[0])
          t := s
          // the slice s struct can be garbage collected
          // the slice s underlying array can not be garbage collected
          fmt.Println("t", cap(t), len(t), s, &t[0])
          p := &t[0]
          // the slice t struct can be garbage collected
          // the slice t (slice s) underlying array can not be garbage collected
          fmt.Println("p", p, *p)
          // the pointer p can be garbage collected
          // the slice t (and s) underlying array can be garbage collected
      }
      

      游乐场:https://play.golang.org/p/PcB_IS7S3QE

      输出:

      s 6 6 [2 3 5 7 11 13] 0x10458000
      t 6 6 [2 3 5 7 11 13] 0x10458000
      p 0x10458000 2
      

      阅读:

      The Go Blog: Go Slices: usage and internals

      The Go Blog: Arrays, slices (and strings): The mechanics of 'append'

      The Go Programming Language Specification : Slice typesSlice expressions

      【讨论】:

        最近更新 更多