返回

Go语言中的slice函数参数传递的本质

闲谈

最近在学习Go语言的过程中,发现一个有意思的事情,有的文章说函数调用传参时 slice 是引用传递,有的说是值传递。为什么同一个东西大家会不同认识?为了搞清楚其本质,我进行了以下内容的研究:

问题一、slice是值传递还是引用传递?

根据Go语言官方文档的定义,slice是引用类型,这意味着它存储的是对底层数组的引用。当我们把slice作为函数参数传递时,实际上是把slice的引用传递给了函数。因此,函数内部对slice的任何修改都会反映到原始slice中。

问题二、为什么有的文章说slice是值传递?

因为slice虽然是引用类型,但它在函数调用时却表现得像值类型。这是因为Go语言编译器在函数调用时会对slice进行一次复制,然后把复制后的slice传递给函数。因此,函数内部对复制后的slice的任何修改都不会反映到原始slice中。

示例代码

package main

import "fmt"

func main() {
    // 定义一个slice
    s := []int{1, 2, 3}

    // 把slice作为参数传递给函数
    modifySlice(s)

    // 打印原始slice
    fmt.Println(s) // 输出:[1 2 3]

    // 定义一个slice,并使用make函数初始化
    s2 := make([]int, 3)
    copy(s2, s) // 把s中的元素拷贝到s2中

    // 把slice作为参数传递给函数
    modifySlice2(s2)

    // 打印原始slice
    fmt.Println(s2) // 输出:[4 5 6]
}

// 函数修改slice
func modifySlice(s []int) {
    s[0] = 4
    s[1] = 5
    s[2] = 6
}

// 函数修改slice
func modifySlice2(s []int) {
    s[0] = 4
    s[1] = 5
    s[2] = 6
}

在上面的示例代码中,modifySlice函数接收一个slice作为参数,并在函数内部修改了slice的元素。然后,我们打印原始slice,发现原始slice并没有被修改。这是因为Go语言编译器在函数调用时对slice进行了复制,函数内部对复制后的slice的修改不会反映到原始slice中。

而在modifySlice2函数中,我们使用了make函数初始化了一个新的slice,并使用copy函数把原始slice中的元素拷贝到新的slice中。然后,我们把新的slice作为参数传递给函数,并在函数内部修改了新的slice的元素。然后,我们打印原始slice,发现原始slice被修改了。这是因为copy函数在把元素拷贝到新的slice时,实际上是把元素的值拷贝到了新的slice中,而不是把元素的引用拷贝到了新的slice中。因此,函数内部对新的slice的修改会反映到原始slice中。

结论

  • slice是引用类型,但在函数调用时却表现得像值类型。
  • 这是因为Go语言编译器在函数调用时会对slice进行一次复制,然后把复制后的slice传递给函数。
  • 函数内部对复制后的slice的任何修改都不会反映到原始slice中。
  • 如果想在函数内部修改原始slice,则需要使用make函数和copy函数来创建一个新的slice,然后把新的slice作为参数传递给函数。