英文:
Passing pointers vs. value in Go
问题
我已经阅读了这篇有趣的博文,与Go语言无关。其中作者提到的一句话引起了我的注意,如下所述:
例如,将通道作为函数的参数传递是高效的,因为在Go中,通道就像是指向C语言中实现的通道数据结构的指针一样简单。对于映射(map)和其他一些类型也是如此。但是,将数组或结构体作为参数传递是低效的;相反,我们应该传递指向这些类型的指针。
为什么在使用一些Go内部类型或结构体时传递指针是低效的呢?
英文:
I've read this interesting blog post that is totally unrelated to Go and one thing the author said caught my attention, this following quote:
> ... For example, it is efficient to pass a channel as the parameter of a function all, because a channel in Go is as simple as a pointer to the channel data structure implemented in C. It is the same for map and some other types. But it is inefficient to pass an array or struct; instead, we should pass pointers to these types.
Why is it inefficient to pass pointers when using some of Go internal types or structs?
答案1
得分: 14
这里的上下文很重要:
然而,Go的类型系统非常复杂;程序员需要在正确使用标准类型之前了解所有细节。例如,它是高效的...
作者指出,当复制映射和通道时,它们看起来像值,但在行为上像指针。
对于其他数据类型,参数中有一个*
,这是一个明确的信号,表明它可能会被就地修改。通常参数前面还有一个&
,这是另一个表明参数正在被修改的信号。
当传递映射和通道时,这些语法信号就不存在了。这导致了意外的结果,就像这个例子中的结果一样:
http://play.golang.org/p/lS1FXZnxb8
类似的批评也可以应用于数组(如[256]byte
)和切片(如[]byte
)之间的巨大差异,其中大小的缺失是不同复制行为的唯一信号。
除了这一切之外,作者错误地将复制和低效等同起来。的确,复制有时需要比传递指针更多的CPU周期或内存访问。然而,并不总是这样。这取决于结构的大小和编译器执行的优化。
选择复制(即传递值)还是传递指针也取决于参数是否可能被函数修改。
对于不打算被函数修改的小结构和数组,通过值传递它们。这消除了由于意外就地修改而引起的整类错误,并且比其他语言中使用的const
更好,因为没有办法欺骗它。当然,对于嵌入指针(包括映射和切片)一定要小心,因为它们可能在函数内部被修改。
英文:
The context is relevant here:
> However, the type system of Go is so complex; programmers need to know
> all the details about the implementation of standard types, before
> they can use them correctly. For example, it is efficient ...
The author is saying that maps and channels look like values but act like pointers when they are copied.
For other datatypes, the parameter has a *
in it, which is a clear signal it could be modified in place. Often the argument has a &
in front as well, which is another signal that the argument is being modified.
When passing maps and channels, those syntactic signals are not there. That leads to unexpected results like this one:
http://play.golang.org/p/lS1FXZnxb8
A similar criticism could be applied to the big difference between arrays like [256]byte
and slices like []byte
, where the absence of a size is the only signal of different copying behavior.
All of this being aside, the author incorrectly equates copying and inefficiency. It is true that copying sometimes requires more CPU cycles or memory accesses than passing a pointer. However, that is not always the case. It depends on the size of the structure and optimizations performed by the compiler.
The decision to copy, i.e. pass a value, vs. pass a pointer is also about whether the argument might be modified by the function.
For small structures and arrays which are not intended to be modified by the function, pass them by value. This eliminates an entire class of bugs caused by accidental modifications in place, and is even better than const
as used in other languages, because there is no way to cheat and get around it. Of course, always be careful with embedded pointers, including maps and slices, because those may still be modified inside the function.
答案2
得分: 6
传递指针并不是低效的。但作者是正确的:因为数组和结构体默认是按值传递的,它们的内容会被复制到每个新的函数调用中,这可能是低效的。
根据《Go切片:用法和内部原理》的文章,数组是按值传递的(切片在底层使用指向数组的指针,因此传递切片更高效)。
与切片类似,通道是通过make
函数分配的(至少是隐式地),因此通道充当对实际数据结构的引用。
因此,当您使用结构体和实际数组时,通常会传递它们的引用。有关此问题的更多详细信息,请参阅Sean的答案(他指出复制并不总是低效的,有时是有意义的)。
英文:
It's not inefficient to pass pointers. But the author is correct: because arrays and structs are passed by value by default, their contents are copied to each new function call. This can be inefficient.
According to Go slices: usage and internals, arrays are passed by value. (Slices use a pointer to the array under the hood, so they're more efficient to pass.)
Like slices, channels are allocated by make
(at least implicitly) and so channels act as a reference to the actual data structure.
So when you're working with structs and actual arrays, typically you'll pass their reference instead. (See Sean's answer for more detail about this. He makes a good point that copying isn't always inefficient. Sometimes it is desirable.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论