切片是按值传递的吗?

新手上路,请多包涵

在 Go 中,我正在尝试为我的旅行商问题制作一个 scramble slice 函数。在这样做的时候,我注意到当我开始编辑切片时,我每次传入的打乱函数都是不同的。

经过一些调试后,我发现这是由于我在函数内部编辑了切片。但既然 Go 应该是一种“按值传递”的语言,这怎么可能呢?

https://play.golang.org/p/mMivoH0TuV

我提供了一个游乐场链接来说明我的意思。通过删除第 27 行,您将获得与保留它不同的输出,这应该没有什么区别,因为函数应该在作为参数传入时制作自己的切片副本。

有人可以解释这种现象吗?

原文由 duck 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 630
2 个回答

Go 中的一切都是按值传递的,切片也是。但是切片值是一个 header ,描述了支持数组的连续部分,并且切片值仅包含指向实际存储元素的数组的指针。切片值不包括其元素(与数组不同)。

因此,当您将切片传递给函数时,将从该标头创建一个副本,包括指针,该指针将指向相同的后备数组。修改切片的元素意味着修改支持数组的元素,因此共享相同支持数组的所有切片都将“观察到”更改。

要查看切片标头中的内容,请查看 reflect.SliceHeader 输入:

 type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

请参阅相关/可能重复的问题: Performance of function slice parameter vs global variable?

阅读博文: Go Slices:用法和内部结构

请注意,当您将切片传递给函数时,如果函数修改切片的“现有”元素,调用者将看到/观察到更改。如果函数向切片添加新元素,则需要更改切片标头(至少长度,但也可能涉及分配新的后备数组),调用者将看不到(在不返回新切片标头的情况下)。

不适用于地图,因为地图是引擎盖下的指针,如果您将地图传递给函数并且该函数向地图添加新条目,则地图指针不会更改,因此调用者将看到更改后的地图(新条目) 更改后不返回地图。

关于切片和映射,请参阅 Go 中的映射初始化 以及 为什么切片值有时会过时但从不映射值?

原文由 icza 发布,翻译遵循 CC BY-SA 4.0 许可协议

你可以在下面找到一个例子。简而言之,切片也是按值传递的,但原始切片和复制的切片链接到相同的底层数组。如果其中一个切片发生变化,则底层数组发生变化,然后其他切片发生变化。

 package main

import "fmt"

func main() {
    x := []int{1, 10, 100, 1000}
    double(x)
    fmt.Println(x) // ----> 3 will print [2, 20, 200, 2000] (original slice changed)
}

func double(y []int) {
    fmt.Println(y) // ----> 1 will print [1, 10, 100, 1000]
    for i := 0; i < len(y); i++ {
        y[i] *= 2
    }
    fmt.Println(y) // ----> 2 will print [2, 20, 200, 2000] (copy slice + under array changed)
}

原文由 Arin Yazilim 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题