下面的代码片段是优先级队列的推送方法的库实现。我想知道为什么代码a = a[0 : n+1]
的行不会抛出越界错误。
func (pq *PriorityQueue) Push(x interface{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
// To simplify indexing expressions in these methods, we save a copy of the
// slice object. We could instead write (*pq)[i].
a := *pq
n := len(a)
a = a[0 : n+1]
item := x.(*Item)
item.index = n
a[n] = item
*pq = a
}
答案 0 :(得分:3)
切片不是数组;它是现有数组的视图。有问题的切片由比自身大的数组支持。定义现有切片的切片时,实际上是切片基础数组,但引用的索引是相对于源切片的。
那是满口的。让我们通过以下方式证明这一点:我们将创建一个零长度的切片,但我们将强制底层数组更大。使用make
创建切片时,第三个参数将设置基础数组的大小。表达式make([]int, 0, 2)
将分配一个大小为2的数组,但它的计算结果为大小为零的切片。
package main
import ("fmt")
func main() {
// create a zero-width slice over an initial array of size 2
a := make([]int, 0, 2)
fmt.Println(a)
// expand the slice. Since we're not beyond the size of the initial
// array, this isn't out of bounds.
a = a[0:len(a)+1]
a[0] = 1
fmt.Println(a)
fmt.Println(a[0:len(a)+1])
}
see here。您可以使用cap
关键字来引用支持给定切片的数组的大小。
您询问的有关在调用上下文中循环cap(pq)
的特定代码(container / heap / example_test.go第90行)。如果您修改呼叫站点上的代码并尝试将另一个项目推入队列,它将像您期望的那样恐慌。我...可能不会建议像这样编写代码。虽然标准库中的代码会执行,但如果我在代码库中找到它,我会非常讨厌。使用append
关键字通常更安全。
答案 1 :(得分:2)
因为它适用于特定的示例程序。以下是原始/完整example source)
中的重要部分const nItem = 10
和
pq := make(PriorityQueue, 0, nItem)
和
for i := 0; i < cap(pq); i++ {
item := &Item{
value: values[i],
priority: priorities[i],
}
heap.Push(&pq, item)
}
答案 2 :(得分:1)
这是container/heap的例子吗?如果是,那么它不会引发异常,因为容量足够大(请参阅如何使用Push
方法)。如果您将示例更改为Push
更多项目然后更改容量,那么它将会抛出。
答案 3 :(得分:0)
一般来说;它不在container/heap
示例中。这是我前段时间给你的一般修复。
func (pq *PriorityQueue) Push(x interface{}) {
a := *pq
n := len(a)
item := x.(*Item)
item.index = n
a = append(a, item)
*pq = a
}