600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > go学习笔记(10)切片(3)声明时的注意点及切片表达式

go学习笔记(10)切片(3)声明时的注意点及切片表达式

时间:2019-06-01 11:29:59

相关推荐

go学习笔记(10)切片(3)声明时的注意点及切片表达式

有多种方式可以声明切片,那么不同的声明之间有什么需要注意的呢?

nil切片与长度为0的切片

nil切片:

var data []int

长度为0的切片:

var x = []int{}

它们两个有细微的差别,长度为0的切片在与nil比较时会返回false。在切片与JSON的转换中会用长度为0的切片来表示空切片。

声明切片时赋默认值

data := []int{1, 2, 3, 4}

如果在声明时知道切片所需的大小,但不知道默认值给什么时,最好用make。根据所声明切片的长度和容量,可分为三种情况:

如果切片被用作缓冲区(buffer),那切片的长度应为非零值。如果你明确知道你所需的切片大小,则切片长度和容量可设为一样的。其他情况下,用make创建一个长度为0,容量为指定值的切片即可。要添加元素时用append就好。

切片表达式

切片表达式可以在现有的切片的基础上创建子切片。它和python中的有点类似,不过不支持负数索引。语言描述不太直观,用代码演示更容易理解:

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)

输出:

a: [1 2 3 4]b: [1 2]c: [2 3 4]d: [2 3]e: [1 2 3 4]

共享存储的切片

通过切片表达式得到的子切片是与原切片共享存储的,改变其中的元素会使原切片和子切片中的数据一起变化。

a := []int{1, 2, 3, 4}b := a[:2]c := a[1:]a[1] = 20b[0] = 10c[1] = 30fmt.Println("a:", a)fmt.Println("b:", b)fmt.Println("c:", c)

输出:

a: [10 20 30 4]b: [10 20]c: [20 30 4]

如果使用append向新切片追加元素的话,同样也会对原切片产生影响:

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]=30b = append(b, 30)// c的长度为3,容量为3,追加元素后,发生扩容,// 与a、b不再共享存储c = append(c, 50)// a的长度为4,容量为4,追加元素后,发生扩容,// 与b不再共享存储a = append(a, 60)// 此时再改变a、b切片中元素的值已经不会相互影响了a[0] = 80b[1] = 90fmt.Println("a:", a)fmt.Println("b:", b)fmt.Println("c:", c)fmt.Println("cap after:", cap(a), cap(b), cap(c))

输出:

cap before: 4 4 3a: [80 2 30 4 60]b: [1 90 30]c: [2 30 4 50]cap after: 8 4 6

因此,为了减少bug,在使用切片表达式创建的子切片时,最好不用append

如果实在需要用append,那最好用全切片表达式来创建子切片。

全切片表达式在原来两个偏移量的基础上增加了一个表示子切片容量偏移量的参数。有点绕,看代码就清楚了:

a := []int{1, 2, 3, 4}b := a[:2:2]c := a[2:3:3]a[1] = 20fmt.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)

输出:

cap before: 4 2 1a: [1 20 3 4]b: [1 20]c: [3]cap after: 8 4 2a: [1 20 3 4 5]b: [1 20 6]c: [3 7]

可以看到,通过第三个参数限制了子切片与原切片共享容量的结束位置,这样在对b使用append时,因为b的容量已经和其长度一致了,append会申请新的内存,避免了对原数据的意外覆盖。

欢迎关注我的微信公众号江达小记

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。