go学习笔记(10)切片(3)声明时的注意点及切片表达式
有多种方式可以声明切片,那么不同的声明之间有什么需要注意的呢?
nil切片与长度为0的切片
nil切片:
1 | var data []int |
长度为0的切片:
1 | var x = []int{} |
它们两个有细微的差别,长度为0的切片在与nil
比较时会返回false
。在切片与JSON的转换中会用长度为0的切片来表示空切片。
声明切片时赋默认值
1 | data := []int{1, 2, 3, 4} |
如果在声明时知道切片所需的大小,但不知道默认值给什么时,最好用make
。根据所声明切片的长度和容量,可分为三种情况:
- 如果切片被用作缓冲区(buffer),那切片的长度应为非零值。
- 如果你明确知道你所需的切片大小,则切片长度和容量可设为一样的。
- 其他情况下,用
make
创建一个长度为0,容量为指定值的切片即可。要添加元素时用append
就好。
切片表达式
切片表达式可以在现有的切片的基础上创建子切片。它和python中的有点类似,不过不支持负数索引。语言描述不太直观,用代码演示更容易理解:
1 | a := []int{1, 2, 3, 4} |
输出:
1 | a: [1 2 3 4] |
共享存储的切片
通过切片表达式得到的子切片是与原切片共享存储的,改变其中的元素会使原切片和子切片中的数据一起变化。
1 | a := []int{1, 2, 3, 4} |
输出:
1 | a: [10 20 30 4] |
如果使用append向新切片追加元素的话,同样也会对原切片产生影响:
1 | a := []int{1, 2, 3, 4} |
输出:
1 | cap before: 4 4 3 |
因此,为了减少bug,在使用切片表达式创建的子切片时,最好不用append
。
如果实在需要用append
,那最好用全切片表达式来创建子切片。
全切片表达式在原来两个偏移量的基础上增加了一个表示子切片容量偏移量的参数。有点绕,看代码就清楚了:
1 | a := []int{1, 2, 3, 4} |
输出:
1 | cap before: 4 2 1 |
可以看到,通过第三个参数限制了子切片与原切片共享容量的结束位置,这样在对b
使用append
时,因为b
的容量已经和其长度一致了,append
会申请新的内存,避免了对原数据的意外覆盖。