GO函数传参 []int 与 [3]int 有何区别?

例子1

func change(arr [3]int) {
  arr[1] = 0
}

func main() {
  arr := [3]int{1,2,3}
    change(arr)
    fmt.Print(arr) // [1,2,3]
}

例子2

func change(arr []int) {
  arr[1] = 0
}

func main() {
  arr := []int{1,2,3}
    change(arr)
    fmt.Print(arr) // [1,0,3]
}

在例子1和2中,仅是[]int[3]int的区别,为何例子1的行为是修改arr的副本,而例子2就是修改arr的本身呢?应该怎么理解这个现象?

阅读 8.8k
3 个回答

好像懂了,不知道是不是可以这么理解:

[]int 是 切片类型
[3]int 是 数组类型

例子2中,change函数里的虽然是按值传递了一个切片副本arr,但是其指向的底层数组是同一个,依然能通过这个切片副本arr去改写其底层数组的值;而例子1中仅仅是传递了一个数组副本arr。

Go语言中函数的参数有两种传递方式,按值传递和按引用传递。Go默认使用按值传递来传递参数,也就是传递参数的副本。在函数中对副本的值进行更改操作时,不会影响到原来的变量。

按引用传递其实也可以称作”按值传递”,只不过该副本是一个地址的拷贝,通过它可以修改这个值所指向的地址上的值。

Go语言中,在函数调用时,引用类型(slice、map、interface、channel)都默认使用引用传递

题主回答的没错,数组传递的数组副本,而切片传递的是切片副本,Go 中只有值传递。但由于两个切片指向的是同一个底层数组,所以修改副本中的某个元素同样会影响到原始的切片。

切片的原始结构在 runtime/slice.go 中,可以了解下。如下所示,slice 包含了指向一个底层数组的指针。

// slice
type slice struct {
    array unsafe.Pointer
    len   int
    cap   int
}

一些回答中提到了 Go 中的引用。其实,这是一个非常有争议性的话题,并没有一个完全的定论。我最近刚写过一篇文章,一文理清 Go 中引用的常见疑惑,对比介绍了 Go 的引用与其他语言的情况。

本来名词是为了帮助别人容易理解记忆的,但 Go 的引用是那么让人难捉摸。我花了好久的时间写了这篇文章,最终的结论,我就不要给引用做啥定义了,会使用它们就好了。

这还有英文文章,About the terminology "reference type" in Go 介绍 Go 的引用,原来,官方曾经想过要将引用从 Go 中移除,可见它是多么讨人厌。

推荐问题
宣传栏