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