slice 在函数方法中传递的问题

slice append

func sliceModify(slice []int) {
    slice = append(slice, 6)
}
func main() {
    slice := []int{1, 2, 3, 4, 5}
    sliceModify(slice)
    fmt.Println(slice)    //[1 2 3 4 5]
}

直接使用切片传递时,仍然传递的是值语义,相当于传入的指针是内存A的,但是append之后因为容量不足,会新建一个slice,将原slice元素复制过去,再添加append要添加的值,会产生一个新的指针指向内存B,但是之前main函数的指针仍然是内存A的,所以A的值仍然不变。

image

要解决这个问题的方法就是传入切片指针

func sliceModify(slice *[]int) {
    *slice = append(*slice, 6)
}
func main() {
    slice := []int{1, 2, 3, 4, 5}
    sliceModify(&slice)
    fmt.Println(slice)   //[1 2 3 4 5 6]
}

直接修改slice中的元素

func sliceModify(slice []int) {
     slice[0] = 88
}
func main() {
    slice := []int{1, 2, 3, 4, 5}
    sliceModify(slice)
    fmt.Println(slice)    //[88 2 3 4 5]
}

当直接修改传递进来的slice切片值得时候,长度没有变,不会生成新的切片内存指针,所以这个时候修改下标为0的元素时,是直接在原内存A的地址直接修改,main函数的指针也是引用的原指向内存A的指针,所有main函数中的slice元素也改变了。

image

一个比较经典的例子

func FuncSlice(s []int, t int) {
    s[0]++  
    s = append(s, t)   
    s[0]++      
}
func main() {
    a := []int{0, 1, 2, 3}
    FuncSlice(a, 4)
    fmt.Println(a) //[1,1,2,3]
}

详细解释一下:

  1. 第一个s[0]++时,s的长度和容量都不用变,相当于在原内存地址A上直接改变
  2. s = append(s, t)时,因为原切片的容量是4,不足以再添加一个新的元素,所以append()函数会创建一个新的底层数组来储存新元素,这个数组的容量是之前数组的两倍,之前是4,现在就是8,随后将原数组的元素复制到新建的数组,同时将append()要添加的元素添加进去,返回一个新的slice。类似本文第一个图。
  3. 等到第二个s[0]++时,这时其实这个slice与之前main()函数的slice已经毫无关系了,新的slice并没有引用旧的原始底层数组,所以这个s[0]++并不能让main()函数中的slice元素改变

那么如果原slice中的容量足够的情况下append()添加一个新的元素呢,那么也会产生一个新的slice指针,但是这个slice引用的之前旧的slice的底层数组,只是新添加了一个元素,如果改变新旧slice共用的一个元素,那么两个slice的值都会改变,如下图

image

一般容量足够的slice都是make定义的,直接:= []int的长度和容量都是根据初始值定义的,如果没有就都是0

slice扩容时,容量翻倍,之前容量为4,扩容后新的slice容量就是8

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容