Go

Go陷阱之切片扩容

2021-04-20  本文已影响0人  Dakini_Wind

go语言的切片实在是好用,但如果不了解它的坑,代码分分钟变为火葬场。
这里记录一下切片扩容所可能犯的错误。

1. 函数中的切片扩容

func Append(nums []int, a int) {
    nums = append(nums, a)
}

func main() {
    nums := []int{1, 2, 3}
    Append(nums, 4)
    fmt.Println(nums)
}

output:

[1 2 3]

解析:

当切片存储容量不足时,则会把当前切片进行扩容并产生新的切片。新的切片具有新的地址,但是go是按值传递,所以新的切片地址只是局部变量,是不能够随着函数返回的。
若切片容量足够,切片作为函数传参扩容后返回的还是原来的切片内容。因为决定切片的还有len

其他:

func Set(nums []int) {
    nums[0] = 999
}

func main() {
    nums := make([]int, 3, 4)
    nums[2] = 2
    Set(nums)
    fmt.Println(nums)
}

output:

[999 0 2]

解析:

切片作为参数传递时,传递的本质还是指向实际存储的数组的地址,所以还是能够改变值的。只是在进行append操作时需要注意。

2. append后赋值给匿名变量

func main() {
    nums := []int{1, 2, 3}
    _ = append(nums, 4)
    fmt.Println(nums)
}

output:

[1 2 3]

解析:

append产生一个新切片,但是因为赋值给了匿名变量,所以不会影响原变量。

3. 空切片

func main() {
   var errs []error
   errs = append(errs, nil)
   fmt.Println(errs)
}

output:

[<nil>]

解析:

nil也是一个值,可以对数组进行追加。需要注意的是实际使用时可能存在风险。

4. 对同一个数组进行多次append操作

dfs相关算法中会经常进行这种操作,需要额外注意。

func main() {
    x := make([]int, 0, 10)
    x = append(x, 1, 2, 3)
    y := append(x, 4)
    z := append(x,5)
    fmt.Println(x)
    fmt.Println(y)
    fmt.Println(z)
}

output:

[1 2 3]
[1 2 3 5]
[1 2 3 5]

解析:

数组xcap为10,在进行append操作后,len变为3。
再在x上进行 append 4 赋值给y,此时因为xcap大于4,所以不需要进行扩容,会在x的基础上进行追加。即xy实际用于存储的数组指向同一块内存。
此时 y为[1 2 3 4],但此时x为[1 2 3],因为决定切片的还有len,尽管内存中实际存储的还有4。
再在x上进行 append 5 赋值给zx这块内存数组的第4位将存为5,覆盖原来的4(因为xlen为3,append的为第4位)。
这时候x依然受len影响不会变,z变为[1 2 3 5]。此时,xyz实际进行存储的数组指向了同一块内存。所以z进行append时会影响y,此时y也为[1 2 3 5]。

上一篇下一篇

猜你喜欢

热点阅读