有时候切片为什么有时候通过引用传递,有时候通过指针传递?

huangapple go评论86阅读模式
英文:

Why is the slice sometimes passed by reference, sometimes by pointer?

问题

在Go语言的默认container/heap包中,有一个实现优先队列的示例。

在查看示例代码时,它使用了一个切片[]*Item,并实现了heap.Interface接口。

我对以下部分有疑问。为什么有些函数声明中优先队列是一个切片,而有些函数声明中优先队列是一个切片的指针?:

func (pq PriorityQueue) Swap(i, j int) {...}
// vs
func (pq *PriorityQueue) Push(x interface{}) {...}

为什么不总是使用(pq PriorityQueue)?在这个关于指向切片的StackOverflow线程中,文档说切片是引用类型,那么为什么要在它们上面使用指针?我对官方文档说了一件事然后混合使用两者而没有解释添加指针的意义感到困惑。

谢谢你的见解!

编辑
这里有一个示例:

// 来自文档的原始示例代码:
func (pq *PriorityQueue) Push(x interface{}) {
    n := len(*pq)
    item := x.(*Item)
    item.index = n
    *pq = append(*pq, item)
}

// 这个是否相同(去掉了切片的指针)?
func (pq PriorityQueue) Push(x interface{}) {
    n := len(pq)
    item := x.(*Item)
    item.index = n
    pq = append(pq, item)
}

如果这两个函数是相同的,为什么现在要使用指针?

英文:

In the default container/heap package in go, there's an example for implementing a priority queue.

While looking at the sample code, it uses a slice []*Item, and implements the heap.Interface.

My trouble is with the following bit. Why are some functions declared with the priority queue as a slice and sometimes as a pointer to slice ?:

func (pq PriorityQueue) Swap(i, j int) {...}
// vs
func (pq *PriorityQueue) Push(x interface{}) {...}

Why isn't it always (pq PriorityQueue) ? On this other StackOverflow thread about pointer to slices, the docs say that slices are refence types, so why use pointers on them ? I'm having trouble with the fact that the official doc says something then mixes both without explaining the point of adding a pointer.

Thanks for your insights !

EDIT:
Here's an example:

// original sample code from the docs:
func (pq *PriorityQueue) Push(x interface{}) {
	n := len(*pq)
	item := x.(*Item)
	item.index = n
	*pq = append(*pq, item)
}

// is this the same (removed pointers to slice) ?
func (pq PriorityQueue) Push(x interface{}) {
	n := len(pq)
	item := x.(*Item)
	item.index = n
	pq = append(pq, item)
}

If both functions are the same, why use a pointer now ?

答案1

得分: 6

这篇文章在Go博客上解释了为什么要这样做。

将切片传递给函数这一节中:

需要明白的是,尽管切片包含一个指针,但它本身是一个值。在底层,它是一个包含指针和长度的结构体值。它不是指向结构体的指针。

因此,如果你想要通过append函数修改切片,你要么需要传递一个指针,要么需要将切片作为值返回。

如果你只想修改切片的内容,你可以简单地按值传递切片:

尽管切片头部是按值传递的,但头部包含一个指向数组元素的指针,因此原始切片头部和传递给函数的头部副本都描述了同一个数组。因此,当函数返回时,可以通过原始切片变量看到修改后的元素。

使用append函数会修改切片的头部。并且

因此,如果我们想要编写一个修改切片头部的函数,我们必须将其作为结果参数返回。

或者:

另一种修改切片头部的方法是传递一个指针给它。

英文:

This article on the Go blog explains why.

From the section Passing slices to functions:

> It's important to understand that even though a slice contains a
> pointer, it is itself a value. Under the covers, it is a struct value
> holding a pointer and a length. It is not a pointer to a struct.

As a result you either need to pass a pointer or you need to return the slice as a value if you want to modify it with append.

If you just want to modify the contents of a slice you can simply pass the slice by value:

> Even though the slice header is passed by value, the header includes a
> pointer to elements of an array, so both the original slice header and
> the copy of the header passed to the function describe the same array.
> Therefore, when the function returns, the modified elements can be
> seen through the original slice variable.

With append you are modifying the slice header. And

> Thus if we want to write a function that modifies the header, we must
> return it as a result parameter

Or:

> Another way to have a function modify the slice header is to pass a
> pointer to it.

huangapple
  • 本文由 发表于 2015年5月7日 20:00:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/30100461.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定