要在参数

时间:2017-12-01 09:37:25

标签: dictionary go reference slice

在golang中,切片和贴图都是引用类型。当您只需要修改切片/贴图中的元素时,切片/贴图成员的修改将被广播"到所有切片。例如,给定m1 := make(map[int]int); m2 := m1m1[3] = 5将导致m2[3] == 5

但是,当您尝试将新元素添加到这两种类型时,情况开始有所不同。如下例所示,添加到地图参数中的新元素将自动显示在参数中;然而,添加到切片中的新元素被丢弃了#39;在论证中。

问题是,为什么会有这种差异?

func editMap(m map[int]int) {
    m[3] = 8
    m[4] = 9
}

func editSlice(s []int) {
    s = append(s, 5)
    s = append(s, 9)
}

func main() {
    m := make(map[int]int, 0)
    m[1] = 5
    m[2] = 3
    fmt.Printf("%v\n", m)  //map[1:5 2:3]
    editMap(m)
    fmt.Printf("%v\n", m)  //map[1:5 2:3 3:8 4:9]

    s := make([]int, 2)
    s[0] = 2
    s[1] = 5
    fmt.Printf("%v\n", s)  //[2 5]
    editSlice(s)
    fmt.Printf("%v\n", s)  //[2 5]
}

编辑: 我可能不清楚这个问题的意图,请让我改一下(对不起,感谢所有内部细节)。

我真正想要的是,显然map是作为指向隐藏哈希映射的所有细节的指针实现的;为什么没有同样的切片实现?

当前的切片实现确实非常轻量级,但是,从API的角度来看(像我们这样的golang用户和像Ross Cox这样的golang维护者之间的API),这两个参考的API都是这样的。类型不是那么统一,可能会导致新手golang的陷阱。

3 个答案:

答案 0 :(得分:5)

行为上的差异在于这些类型的实施。

映射是指向数据结构的指针,而切片是小结构,其中包含指向后备阵列的指针,切片长度和容量。 reflect.SliceHeader为切片标头建模:

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

这是偏差的主要原因:指针与结构。

向地图添加新元素时,指针保持不变。指向的地图结构可能会改变,但地图指针不会改变。

更改切片的元素时,可以有效地更改支持数组的元素。切片头不会改变:它将继续保持相同的指针,长度和容量。切片值的任何副本(切片标题)都指向同一个支持数组,因此,所有切片都将看到更改的元素。

向切片添加新元素时,描述 new 切片(包含附加元素)的切片标头必须更改:长度必须增加1,并且可选地指针和容量也可以改变(如果必须分配新的后备阵列以容纳新元素)。

Go中的所有内容都按值传递。这是偏离的第二个原因。传递切片时,会从头部创建一个副本,如果将某些内容附加到副本上,即使将结果正确地分配给它(返回到副本),原始切片头也不会知道。

传递地图时,也会复制地图值(指针),但原始地图指针和复制地图指针都将指向相同的地图结构。通过任何这些指针添加值或更改地图将改变唯一的指向地图结构。

要使它们的行为相同,您必须使它们与类型的“type”相同,即:指针。如上所述,地图已经是(隐藏的)指针。如果你继续开始向指针传递指针,并且你对指向值进行操作,它们的行为与地图相同。在实践中,这很少使用(有助于使用切片指针而不是数组指针的语言支持更少),而是替代方法在返回新切片的地方广泛传播。您可以在此处详细了解:Slicing a slice pointer passed as argument

答案 1 :(得分:1)

要修改切片,您只需编辑这样的代码(https://play.golang.org/p/2SeP93itIL):

func editSlice(s *[]int) {
    *s = append(*s, 5)
    *s = append(*s, 9)
}

Effective Go中有一些解释:

  

如果某个函数采用切片参数,则更改它会使转换为   切片的元素对于调用者

是可见的

这就是为什么切片的修改参数对调用者可见,但是在您返回或分配给现有变量之前,新切片本身是不可见的。因为append会返回一个新切片

  

之后我们必须返回切片,因为虽然Append可以   修改切片的元素,切片本身(运行时数据   持有指针,长度和容量的结构)被传递   值

答案 2 :(得分:0)

如果两个对象具有相同的内存地址,那么如果更改其中一个对象的值,则另一个将更改,因为它们实际上是一个对象。

在您的代码中,m中的editMapm中的main具有相同的值:相同字典对象的地址。但是,s中的editSlices中的main的值是两个不同的对象。

我希望我的解释足够明确。