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]
}
详细解释一下:
- 第一个
s[0]++
时,s的长度和容量都不用变,相当于在原内存地址A上直接改变 - 当
s = append(s, t)
时,因为原切片的容量是4,不足以再添加一个新的元素,所以append()
函数会创建一个新的底层数组来储存新元素,这个数组的容量是之前数组的两倍,之前是4,现在就是8,随后将原数组的元素复制到新建的数组,同时将append()
要添加的元素添加进去,返回一个新的slice。类似本文第一个图。 - 等到第二个
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