【问题标题】:Go Slice - difference between [:n] and [n:]Go Slice - [:n] 和 [n:] 之间的区别
【发布时间】:2016-08-15 18:23:05
【问题描述】:

Go Slice 问题,如果我遗漏了什么,请查看下方并发表评论。

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

输出:

[3 5 7 11 13] [7 11 13] panic: runtime error: slice bounds out of range

上面说的有道理。

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

输出:

[2] [2 3] [2 3 5 7 11]

这是否也应该从 s=s[:2] 得到数组越界恐慌?

【问题讨论】:

  • 我的问题是 w.r.t 分配,当我们执行 s=s[:n] 时,我的想法是 s 被新切片覆盖,而旧数组\切片实际上已丢失,不再存在.但相反,我继续对已经切片和切块的原始数组进行切片,这似乎与 Java 不同。

标签: arrays go slice


【解决方案1】:

Go 中的子切片允许您在切片末尾之外进行切片,只要它仍在底层数组的容量范围内。您不能在该切片的开头之前进行切片,但您可以在它之后切片,只要您不超过最后分配的索引。

例如,s[3:] 然后s[:3] 有效,但是s[4:] 然后s[:4] 会出现恐慌,因为您正在请求底层数组的索引 4 到 7,而该数组仅分配了索引 0-5。

这有点奇怪,但它确实允许您通过执行 slice = slice[:cap(slice)] 来最大化任何切片。

https://play.golang.org/p/Gq5xoXc3Vd

language specification 对此进行了注释,顺便说一句。我在下面解释了您正在使用的简单切片表示法(还有一种替代方法,它还指定了新切片的最大索引)。

对于字符串、数组、指向数组的指针或切片 a,主表达式 a[low : high] 构造一个子字符串或切片。 如果 0

【讨论】:

  • 谢谢,我的问题是 w.r.t 分配,当我们执行 s=s[:n] 时,我的想法是 s 被新切片覆盖,而旧数组\切片实际上丢失了。这对我来说似乎有点不直观。
  • 啊,不,相同的底层数组。从根本上说,切片只是一个 3 字结构,具有指向底层数组的指针、整数长度和整数容量。当您进行子切片时,您可以根据需要使用更新的字段更改该切片“标题”(或创建一个新标题)。当您执行s=s[:n] 时,所做的只是将切片标头的“长度”值更改为n。仍然指向相同的底层数组,仍然具有相同的容量,因此您可以再次将其扩展到该容量。
  • 如果你想复制底层数组(即有一个新切片指向一个完全不同的数组,但其中包含相同的数据),你要么需要手动完成,按索引索引,或使用内置的copy() 函数,该函数执行切片的深层复制。 编辑: 仅深度复制到数组级别。如果您的数组存储指针,则不会被深度复制。
  • 谢谢,这说明了,好的,这里的关键似乎是容量。所以 Go 数组的容量和长度是不同的。所以当我切片时,循环长度比循环容量更好,这是一个正确的说法吗?
  • 哦,好吧 ... copy() 复制一份
【解决方案2】:

我只是想在阅读官方博客后分享我的想法:https://blog.golang.org/slices

这里是 Golang 博客中的 sliceHeader:

type sliceHeader struct {
    Length        int
    ZerothElement *byte
}

现在你声明你的切片:

s := []int{2, 3, 5, 7, 11, 13}

我相信它会做这样的事情:

var array [6]int
slice := sliceHeader {
    Length:        6,
    ZerothElement: &array[0], 
}

通过这样做:s = s[:2],您有效地将 sliceHeader 的长度从 6 更改为 2,因此您的 sliceHeader 将如下所示:

slice := sliceHeader {
    Length:        2,
    ZerothElement: &array[0],
}

请注意 ZerothElement 仍然指向内存中的同一位置。因此,我们可以通过再次更改 Length 来将切片扩展回其原始形式 s = s[:6]

现在,假设您没有执行 s = s[:2] 而是执行了 s = s[2:],您实际上所做的是通过从 Length 中减去 2 并将 ZerothElement 两个索引向前移动来隐藏前两个元素,从而导致 sliceHeader 为:

slice := sliceHeader {
    Length:        4,
    ZerothElement: &array[2],   
}

此时,您无法将切片恢复为其原始形式,因为无法将切片扩展到 ZerothElement 之外。好的,如果假设您可以访问 ZerothElement 之前的任何元素怎么办?然后我们的切片变得未定义,因为它可能是array[0...4]array[1...5]array[2...6]

是的,这就是我认为 [n:] 和 [:n] 行为不同的原因。

【讨论】:

    猜你喜欢
    • 2019-01-28
    • 2011-01-08
    • 1970-01-01
    • 2016-05-03
    • 1970-01-01
    • 1970-01-01
    • 2016-08-16
    • 2017-04-23
    相关资源
    最近更新 更多