golang函数传递slice是值传递还是引用传递?
先说结论:go中所有的函数传参都为值传递
在 Go 语言中,切片(slice)是引用类型,因此在函数调用中传递切片时,实际上是传递了切片的引用(地址),而不是切片的副本。这意味着对传递的切片进行修改会影响原始切片的内容。
当将切片作为函数参数传递时,函数接收的是切片的引用。这使得函数可以访问和修改原始切片的元素,而无需进行显式的指针操作。
下面是一个示例代码来说明切片是如何通过引用传递的:
package main
import "fmt"
func modifySlice(s []int) {
s[0] = 100
s = append(s, 200)
}
func main() {
slice := []int{1, 2, 3}
fmt.Println("Before:", slice) // 输出: [1 2 3]
modifySlice(slice)
fmt.Println("After:", slice) // 输出: [100 2 3],这里并没有append的200!!
}
在上面的示例中,我们定义了一个 modifySlice
函数,它接收一个切片作为参数。在函数内部,我们将切片的第一个元素修改为 100,并向切片追加一个新的元素 200。
在 main
函数中,我们创建了一个切片 slice
,并将其传递给 modifySlice
函数。在函数内部,我们修改了切片的第一个元素,并追加了一个新的元素。然后,我们在 main
函数中打印输出原始切片 slice
,可以看到修改已经影响了原始切片的内容。
这表明切片在函数调用中是貌似通过引用传递的,因为函数对切片的修改会影响原始切片。这种行为与值类型(如整数、字符串等)的传递方式不同,值类型在函数调用中是通过值传递的,函数对值类型的修改不会影响原始值。
既然是引用传递,为啥外层slice没有最后一个元素200呢?
slice包含三个成员:指向底层数组的指针,slice的长度,长度的容量
go中所有的函数传参都为值传递,当slice作为入参,并且对slice进行修改时,由于指向数组的指针指向同一个地址,对数组的修改会同步到函数外部的切片上;但是slice的长度和容量是两个值变量,函数内部对他们的修改无法传递到函数外,所以会造成函数内对slice进行append操作,底层数组发生了变化,但是函数外部slice因无法感知内部切片长度的变化,而没有修改的情况;另外,当slice的长度要超出slice的容量时,slice会进行扩容:申请一块新的地址作为底层数组,将旧的数组上的值拷贝过来,扩容完成后,函数内切片指向的数组与函数外切片指向的数组就不再是同一个地址,所有的修改都不会生效。
点击查看代码
func main() {
slice := []int{1, 2, 3}
fmt.Println("Before:", slice) // 输出: [1 2 3]
modifySlice(slice)
fmt.Println("After:", slice) // 输出: [1 2 3]
}
func modifySlice(s []int) {
s = append(s, 4) //长度要超出slice的容量时,slice会进行扩容:申请一块新的地址作为底层数组,将旧的数组上的值拷贝过来,扩容完成后,函数内切片指向的数组与函数外切片指向的数组就不再是同一个地址,后面所有的修改都不会生效
s[0] = 100
fmt.Println("temp:", s) // 输出: [100 2 3 4]
}
slice := []int{1, 2, 3}
fmt.Println("Before:", slice) // 输出: [1 2 3]
modifySlice(slice)
fmt.Println("After:", slice) // 输出: [1 2 3]
}
func modifySlice(s []int) {
s = append(s, 4) //长度要超出slice的容量时,slice会进行扩容:申请一块新的地址作为底层数组,将旧的数组上的值拷贝过来,扩容完成后,函数内切片指向的数组与函数外切片指向的数组就不再是同一个地址,后面所有的修改都不会生效
s[0] = 100
fmt.Println("temp:", s) // 输出: [100 2 3 4]
}
解决方案:将切片的指针作为参数传递进去
func append_test (arr []int) {
arr = append(arr, 555,555)
(arr)[0] = 55 //注意这里的写法 (arr)[] //arr[] 会被编译器理解为*(arr[index])
}