a := []int{1, 2, 3, 4} // 不写起始偏移量则默认从0开始 b := a[:2] // 不写结束偏移量则默认为切片的长度 c := a[1:] // 指定起始和结束偏移量,左闭右开 d := a[1:3] // 都不指定,则起始偏移量为0,结束偏移量为切片长度 e := a[:] fmt.Println("a:", a) fmt.Println("b:", b) fmt.Println("c:", c) fmt.Println("d:", d) fmt.Println("e:", e)
输出:
1 2 3 4 5
a: [1234] b: [12] c: [234] d: [23] e: [1234]
共享存储的切片
通过切片表达式得到的子切片是与原切片共享存储的,改变其中的元素会使原切片和子切片中的数据一起变化。
1 2 3 4 5 6 7 8 9
a := []int{1, 2, 3, 4} b := a[:2] c := a[1:] a[1] = 20 b[0] = 10 c[1] = 30 fmt.Println("a:", a) fmt.Println("b:", b) fmt.Println("c:", c)
a := []int{1, 2, 3, 4} b := a[:2] c := a[1:] // 新切片的容量为老切片容量减去新切片起始索引 fmt.Println("cap before:", cap(a), cap(b), cap(c)) // b的长度为2容量为4,与a、c共享同一存储, // 追加元素后,原来的数据被覆盖 // a[2]=30,c[1]=30 b = append(b, 30) // c的长度为3,容量为3,追加元素后,发生扩容, // 与a、b不再共享存储 c = append(c, 50) // a的长度为4,容量为4,追加元素后,发生扩容, // 与b不再共享存储 a = append(a, 60) // 此时再改变a、b切片中元素的值已经不会相互影响了 a[0] = 80 b[1] = 90 fmt.Println("a:", a) fmt.Println("b:", b) fmt.Println("c:", c) fmt.Println("cap after:", cap(a), cap(b), cap(c))
输出:
1 2 3 4 5
cap before: 443 a: [80230460] b: [19030] c: [230450] cap after: 846
因此,为了减少bug,在使用切片表达式创建的子切片时,最好不用append。
如果实在需要用append,那最好用全切片表达式来创建子切片。
全切片表达式在原来两个偏移量的基础上增加了一个表示子切片容量偏移量的参数。有点绕,看代码就清楚了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
a := []int{1, 2, 3, 4} b := a[:2:2] c := a[2:3:3] a[1] = 20 fmt.Println("cap before:", cap(a), cap(b), cap(c)) fmt.Println("a:", a) fmt.Println("b:", b) fmt.Println("c:", c) a = append(a, 5) b = append(b, 6) c = append(c, 7) fmt.Println("cap after:", cap(a), cap(b), cap(c)) fmt.Println("a:", a) fmt.Println("b:", b) fmt.Println("c:", c)