我读过这篇与 Go 完全无关的有趣博客文章,作者所说的一件事引起了我的注意,以下引用:
...例如,将通道作为函数 all 的参数传递是有效的,因为 Go 中的通道就像指向 C 中实现的通道数据结构的指针一样简单。对于 map 和其他一些类型。但是传递数组或结构是低效的;相反,我们应该传递指向这些类型的指针。
为什么在使用某些 Go 内部类型或结构时传递指针效率低下?
上下文在这里是相关的:
但是,Go 的类型系统非常复杂;程序员需要了解有关标准类型实现的所有细节,然后才能正确使用它们。例如,它是有效的...
作者是说地图和通道看起来像值,但在复制时就像指针。
对于其他数据类型,参数中有一个*
,这是一个明确的信号,它可以就地修改。通常参数&
前面也有一个,这是参数正在修改的另一个信号。
在传递地图和通道时,这些句法信号不存在。这会导致像这样的意外结果:
http://play.golang.org/p/lS1FXZnxb8
类似的批评可以应用于数组 like[256]byte
和 slices like之间的巨大差异[]byte
,其中缺少大小是不同复制行为的唯一信号。
撇开这一切不谈,作者错误地将复制和低效等同起来。确实,与传递指针相比,复制有时需要更多的 CPU 周期或内存访问。然而,情况并非总是如此。它取决于结构的大小和编译器执行的优化。
复制(即传递一个值)与传递一个指针的决定也与参数是否可能被函数修改有关。
对于不打算被函数修改的小型结构和数组,通过 value 传递它们。这消除了由于意外修改导致的一整类错误,甚至比const
其他语言中使用的更好,因为没有办法作弊和绕过它。当然,始终要小心嵌入的指针,包括映射和切片,因为它们仍可能在函数内部被修改。
传递指针并不是低效的。但是作者是对的:因为数组和结构体默认是传值的,所以它们的内容会被复制到每个新的函数调用中。这可能是低效的。
根据Go slices: usage 和 internals,数组是按值传递的。(切片在底层使用指向数组的指针,因此它们的传递效率更高。)
像切片一样,通道是由make
(至少是隐式的)分配的,因此通道充当对实际数据结构的引用。
因此,当您使用结构和实际数组时,通常会改为传递它们的引用。(有关此问题的更多详细信息,请参见肖恩的回答。他提出了一个很好的观点,即复制并不总是低效的。有时它是可取的。)