Golang Go语言中切片作为参数传参,使用append后在函数内切片被修改了,而在主函数里面没有被改变

Golang Go语言中切片作为参数传参,使用append后在函数内切片被修改了,而在主函数里面没有被改变

package main

import “fmt”

func main() { arr := make([]int, 3, 4) //创建一个长度为 3 ,容量为 4 的切片 fmt.Println(arr, len(arr), cap(arr)) //[0 0 0] 3 4 // ----- fmt.Printf("%p\n", arr) addNum(arr) // ----- fmt.Println(arr, len(arr), cap(arr)) //[0 0 0] 3 4 fmt.Printf("%p\n", arr) }

func addNum(sli []int) { fmt.Printf("%p\n", sli) sli = append(sli, 4) fmt.Println(sli, len(sli), cap(sli)) //[0 0 0 4] 4 4 fmt.Printf("%p\n", sli) }

看到网上的解释是, 在 addNum 里面,sli 的底层数组是的确被修改了,可是切片的 len 由于是值复制,所以切片的 len 没有被修改,导致外层 main 里面的切片没有被显示?

如果是这样,那么应该传参的时候传入的切片地址应该不一样才对,因为是传值,传入的是切片结构体的拷贝值,而不应该是切片的原地址。

type slice struct {
	array unsafe.Pointer //存储数组指针
	len   int
	cap   int
}

望大神指点。


更多关于Golang Go语言中切片作为参数传参,使用append后在函数内切片被修改了,而在主函数里面没有被改变的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

你打印的是切片里引用的底层数据的地址,而不是切片本身的地址。实参和形参的切片是不同的切片,只不过它们引用的底层数据是一样的。

更多关于Golang Go语言中切片作为参数传参,使用append后在函数内切片被修改了,而在主函数里面没有被改变的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


谢谢大神回复。
如果按照你这样理解, fmt.Printf("%p\n", arr) , 这个取的并不是切片的指针地址,而且指向底层的地址?
如果要获取切片地址,要 fmt.Printf("%d" , &arr) 这样吗?

在Go语言中,切片作为参数传递给函数时,传递的是切片的引用(即底层数组的指针和切片的长度、容量信息)。因此,如果在函数内部对切片使用append操作,并且append导致切片需要分配新的底层数组时,函数内部的切片将指向新的数组,而原始切片则保持不变。

然而,如果append操作没有导致切片重新分配底层数组(即切片的容量足够容纳新元素),那么函数内部的修改将反映到原始切片上,因为两者仍然共享同一个底层数组。

要避免这种情况下的混淆,有几种策略:

  1. 复制切片:在函数内部,可以先复制切片,再对副本进行操作。这样可以确保原始切片不受影响。

    func modifyCopy(s []int) {
        copySlice := make([]int, len(s))
        copy(copySlice, s)
        copySlice = append(copySlice, 1) // 修改副本,不影响原始切片
    }
    
  2. 返回新切片:让函数返回一个新的切片,这样调用者可以明确地知道何时切片被修改或替换。

    func appendElement(s []int, elem int) []int {
        return append(s, elem)
    }
    

总之,理解切片在Go中的传递机制对于避免意外的副作用至关重要。根据具体需求选择合适的方法,以确保程序的正确性和可维护性。

回到顶部