前面有文章写了数组和切片的区别,一直有点一知半解,今天再来理一下思路。

数组和切片最明显的区别

最明显的区别就是声明上的区别, 数组的声明带上了len, 而切片不需要声明长度,运行时修改长度

  1. var array [2]int // 数组声明
  2. var array [2]int = [2]int{1, 2} //数组声明加初始化
  3. var slice []int //切片
  4. slice := make([]int, 6) // 长度为6的切片

数组是值传递指针传递?

在go中,于C数组变量隐式作为指针传递不同,go数组是值类型,赋值和函数传参操作都会复制整个数组数据

  1. func main() {
  2. arrayA := [2]int{100, 200}
  3. var arrayB [2]int
  4. arrayB = arrayA
  5. fmt.Printf("arrayA : %p , %v\n", &arrayA, arrayA)
  6. fmt.Printf("arrayB : %p , %v\n", &arrayB, arrayB)
  7. testArray(arrayA)
  8. }
  9. func testArray(x [2]int) {
  10. fmt.Printf("func Array : %p , %v\n", &x, x)
  11. }
  12. // arrayA : 0xc4200bebf0 , [100 200]
  13. // arrayB : 0xc4200bec00 , [100 200]
  14. // func Array : 0xc4200bec30 , [100 200]

这会带来什么问题呢?假想每次传参都用数组,那么每次数组都要被复制一遍,非常消耗内存。这就是就需要指针传递了,函数直接传递一个8字节的指针就可以了,非常省内存,切片是引用类型,在go中是没有引用传递的,只有值传递。

切片的数据结构

  1. type slice struct {
  2. array unsafe.Pointer
  3. len int
  4. cap int
  5. }
  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/2332713/1624802912511-3a6172e2-4ade-4365-a49b-687066f794ea.png#clientId=u2e86e4cf-c50e-4&from=paste&height=276&id=u099c3dd6&margin=%5Bobject%20Object%5D&name=image.png&originHeight=458&originWidth=996&originalType=binary&ratio=2&size=21248&status=done&style=none&taskId=ub23ad90a-89e4-4b2f-8274-32f46248e50&width=600)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/2332713/1624803405764-9f57564b-9d19-4a2a-a713-864fd260307b.png#clientId=u2e86e4cf-c50e-4&from=paste&height=401&id=ud71735ba&margin=%5Bobject%20Object%5D&name=image.png&originHeight=802&originWidth=1730&originalType=binary&ratio=2&size=70774&status=done&style=none&taskId=uf3e4d3d4-69c5-4e71-8236-41b18eff5b5&width=865)

创建切片

image.png

切片的扩容策略

  1. slice := []int{10, 20, 30, 40}
  2. newSlice := append(slice, 50)

这个扩容策略,新的切片和之前的切片已经不同了,新切片指向的数组是一个全新的数组。并且 cap 容量也发生了变化。

image.png

切片扩容的bug

但是切片扩容有一个很bug的地方,那就是不一定会产生新的数组,比如说我们append的时候数组还有富余的量,就不会产生一个新的数组。假如现在很多切片执行这个数组,改动数组中的一个元素,全部切片都会修改。我们可以使用深拷贝避免这个bug。

深拷贝

深拷贝确保是新的数组,新的切片。修改动作不会对别的切片产生影响。

举个最简单的例子

  1. func sliceOperator1(slice []int) {
  2. slice = append(slice, 5)
  3. fmt.Println(slice)
  4. fmt.Printf("sliceOperator1 %p\n", &slice[0])
  5. ret = append(ret, slice)
  6. }
  7. func sliceOperator2(slice []int) {
  8. fmt.Printf("sliceOperator2 %p\n", &slice[0])
  9. fmt.Println(slice)
  10. slice = append(slice, 2)
  11. fmt.Println(slice)
  12. }
  13. var ret [][]int
  14. func main() {
  15. ret = [][]int{}
  16. n := 6
  17. arr := make([]int, n)
  18. fmt.Printf("%p\n", &arr[0])
  19. arr = append(arr, 7)
  20. fmt.Printf("%p\n", &arr[0])
  21. sliceOperator1(arr)
  22. fmt.Println(arr)
  23. sliceOperator2(arr)
  24. fmt.Println(arr)
  25. fmt.Println(ret)
  26. }