在 Go 中,我正在尝试为我的旅行商问题制作一个 scramble slice 函数。在这样做的时候,我注意到当我开始编辑切片时,我每次传入的打乱函数都是不同的。
经过一些调试后,我发现这是由于我在函数内部编辑了切片。但既然 Go 应该是一种“按值传递”的语言,这怎么可能呢?
https://play.golang.org/p/mMivoH0TuV
我提供了一个游乐场链接来说明我的意思。通过删除第 27 行,您将获得与保留它不同的输出,这应该没有什么区别,因为函数应该在作为参数传入时制作自己的切片副本。
有人可以解释这种现象吗?
原文由 duck 发布,翻译遵循 CC BY-SA 4.0 许可协议
Go 中的一切都是按值传递的,切片也是。但是切片值是一个 header ,描述了支持数组的连续部分,并且切片值仅包含指向实际存储元素的数组的指针。切片值不包括其元素(与数组不同)。
因此,当您将切片传递给函数时,将从该标头创建一个副本,包括指针,该指针将指向相同的后备数组。修改切片的元素意味着修改支持数组的元素,因此共享相同支持数组的所有切片都将“观察到”更改。
要查看切片标头中的内容,请查看
reflect.SliceHeader
输入:请参阅相关/可能重复的问题: Performance of function slice parameter vs global variable?
阅读博文: Go Slices:用法和内部结构
请注意,当您将切片传递给函数时,如果函数修改切片的“现有”元素,调用者将看到/观察到更改。如果函数向切片添加新元素,则需要更改切片标头(至少长度,但也可能涉及分配新的后备数组),调用者将看不到(在不返回新切片标头的情况下)。
不适用于地图,因为地图是引擎盖下的指针,如果您将地图传递给函数并且该函数向地图添加新条目,则地图指针不会更改,因此调用者将看到更改后的地图(新条目) 更改后不返回地图。
关于切片和映射,请参阅 Go 中的映射初始化 以及 为什么切片值有时会过时但从不映射值?