August Rush

一个还在努力成长的小火汁!

游龙当归海,海不迎我自来也。

We create our own demons.

You can reach me at augustrush0923@gmail.com
slice在作为参数传入函数时的问题
发布:2024年01月12日 | 作者:augustrush | 阅读量: 708

首先,slice本身是个结构体,它内部第一个元素是一个指针类型,指向底层的具体数组。

当它作为参数传递进函数时,会对整个结构体做一个拷贝并产生一个新地址存放这个结构体。但结构体内部的数据并没有发生改变,也就是说拷贝产生的数据的指针类型指向同一个数组。

当这时在函数内部操作修改该slice时,会影响函数外部的slice

package main

import "fmt"

func main() {
    slice := make([]int, 0)
    slice = append(slice, 1, 2, 3)
    modify(slice)
    fmt.Println(slice)
}

func modify(arr []int) {
    arr[0] = 11
}

输出结果:

$go run main.go
[11, 2, 3]

因为slice是引用类型,指向的是同一个数组。在函数内外,arr本身的地址变了,但是两个指针指向的数组地址是不变的。所以在函数内部的修改可以影响到函数外部。

但如果在函数内使用append增加数据时,情况就复杂多了,分两种情况:

  • 当slice没有足够的容量(capacity)

    ```go package main

    import "fmt"

    func main() { arr := make([]int, 0) arr = append(arr, 1, 2, 3) fmt.Println(arr) }

    func appendSlice(arr []int) { arr = append(arr, 4) } ```

    输出结果:

    $ go run main.go [1, 2, 3]

  • 当slice有足够的容量(capacity)

    ```go package main

    import "fmt"

    func main() { arr := make([]int, 0, 5) arr = append(arr, 1, 2, 3) fmt.Println(arr) }

    func appendSlice(arr []int) { arr = append(arr, 4) } ```

    输出结果:

    $ go run main.go [1, 2, 3]

虽然看似输出结果都一样,函数内部进行append后没有造成外部的数据新增。但是原理却是不一样的。

当容量足够的时候 * 在内部调用append的时候,由于cap容量足够,所以不需要扩容,所以函数内操作的数组和函数外的一致。但因为函数外的len是增加之前的数,所以函数外只会遍历到增加之前的位置就停下,但实际上这个数组的长度已经变更。 内存分布如下图:

当容量不足的时候 * 在内部调用append的时候,由于cap容量不足,需要进行扩容,这时函数内操作的数组就是一块新的。所以函数内的操作不会影响函数外的数组。 内存分布如下图:

在函数内修改slice并在外部生效的正确姿势应该是传入指针:

package main

func appendElement(arr *[]int){
    *arr = append(*arr, 1)
}

传指针进去,这样拷贝的就是这个指针,指针指向的对象,也就是slice本身,是不变的,使用*arr可以对slice进行操作。



  • 标签云

  • 支付宝扫码支持一下

  • 微信扫码支持一下



基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建

京ICP备20007446号-1 & 豫公网安备 41100202000460号

网站地图 & RSS | Feed