go语言中的切片,你真的了解吗?

0 前言

切片 slice 是 golang 中一个非常经典的数据结构,其定位可以类比于其他编程语言中的数组. 本文介绍的内容会分为 slice 的使用教程、问题讲解以及源码解析,走读的源码为 go v1.19.

1 几个问题

首先呢,我觉得使用 go 的朋友对于切片这个数据结构不会感到陌生,一些基本的概念和用法应该是可以做到了然于心的. 下面我先抛出一轮问题,大家可以思考并给出自己的答案,然后带着问题进入本文后半段的学习. 在第 2 章中,我们会进行原理补充,把问题涉及的拼图碎片一块块集齐;最后在第 3 章中,我们会正面给出第 1 章中所有问题的答案.

下面,就正式开启灵魂拷问环节:

1.1 问题1

  • • 初始化切片 s 长度和容量均为 10
  • • 在 s 的基础上追加 append 一个元素

请问经过上述操作后,切片s 的内容、长度以及容量分别是什么?

func Test_slice(t *testing.T){
    s := make([]int,10)  
    s = append(s,10)
    t.Logf("s: %v, len of s: %d, cap of s: %d",s,len(s),cap(s))
}

1.2 问题2

  • • 初始化切片 s 长度为 0,容量为 10
  • • 在 s 的基础上追加 append 一个元素

请问经过上述操作后,切片s 的内容、长度以及容量分别是什么?

func Test_slice(t *testing.T){
    s := make([]int,0,10)  
    s = append(s,10)
    t.Logf("s: %v, len of s: %d, cap of s: %d",s,len(s),cap(s))
}

1.3 问题3

  • • 初始化切片 s 长度为 10,容量为 11
  • • 在 s 的基础上追加 append 一个元素

请问经过上述操作后,切片s 的内容、长度以及容量分别是什么?

func Test_slice(t *testing.T){
    s := make([]int,10,11)  
    s = append(s,10)
    t.Logf("s: %v, len of s: %d, cap of s: %d",s,len(s),cap(s))
}

1.4 问题4

  • • 初始化切片 s 长度为 10,容量为 12
  • • 截取切片 s index = 8 往后的内容赋给 s1

求问 s1 的内容、长度以及容量分别是什么?

func Test_slice(t *testing.T){
    s := make([]int,10,12)  
    s1 := s[8:]
    t.Logf("s1: %v, len of s1: %d, cap of s1: %d",s1,len(s1),cap(s1))
}

1.5 问题5

  • • 初始化切片 s 长度为 10,容量为 12
  • • 截取切片 s index 为 [8,9) 范围内的元素赋给切片 s1

求问 s1 的内容、长度以及容量分别是什么?

func Test_slice(t *testing.T){
    s := make([]int,10,12)  
    s1 := s[8:9]
    t.Logf("s1: %v, len of s1: %d, cap of s1: %d",s1,len(s1),cap(s1))
}

1.6 问题6

  • • 初始化切片 s 长度为 10,容量为 12
  • • 截取切片 s index = 8 往后的内容赋给 s1
  • • 修改 s1[0] 的值

请问这个修改是否会影响到 s? 此时,s 的内容是什么?

func Test_slice(t *testing.T){
    s := make([]int,10,12)  
    s1 := s[8:]
    s1[0] = -1
    t.Logf("s: %v",s)
}

1.7 问题7

  • • 初始化切片 s 长度为 10,容量为 12

请问,访问 s[10] 是否会越界?

func Test_slice(t *testing.T){
    s := make([]int,10,12)  
    v := s[10]
    // 求问,此时数组访问是否会越界
}

1.8 问题8

  • • 初始化切片 s 长度为 10,容量为 12
  • • 截取 s 中 index = 8 后面的内容赋给 s1
  • • 在 s1 的基础上追加 []int{10,11,12} 3 个元素

请问,经过上述操作时候,访问 s[10] 是否会越界?

func Test_slice(t *testing.T){
    s := make([]int,10,12)  
    s1 := s[8:]
    s1 = append(s1,[]int{10,11,12}...)
    v := s[10]
    // ...
    // 求问,此时数组访问是否会越界
}

1.9 问题9

  • • 初始化切片 s 长度为 10,容量为 12
  • • 截取切片 s index = 8 往后的内容赋给 s1
  • • 在方法 changeSlice 中,对 s1[0] 进行修改

求问,经过上述操作之后,s 的内容是什么?

func Test_slice(t *testing.T){
    s := make([]int,10,12)  
    s1 := s[8:]
    changeSlice(s1)
    t.Logf("s: %v",s)
}

func changeSlice(s1 []int){
  s1[0] = -1
}

1.10 问题10

  • • 初始化切片 s 长度为 10,容量为 12
  • • 截取切片 s index = 8 往后的内容赋给 s1
  • • 在方法 changeSlice 中,对 s1 进行 apend 追加操作

请问,经过上述操作后,s 以及 s1 的内容、长度和容量分别是什么?

func Test_slice(t *testing.T){
    s := make([]int,10,12)  
    s1 := s[8:]
    changeSlice(s1)
    t.Logf("s: %v, len of s: %d, cap of s: %d",s, len(s), cap(s))
    t.Logf("s1: %v, len of s1: %d, cap of s1: %d",s1, len(s1), cap(s1))
}

func changeSlice(s1 []int){
  s1 = append(s1, 10)
}

1.11 问题11

  • • 初始化切片 s,内容为 []int{0,1,2,3,4}
  • • 截取 s 中 index = 2 前面的内容(不含s[2]),并在此基础上追加 index = 3 后面的内容

请问,经过上述操作后,s 的内容、长度和内容分别是什么?此时访问 s[4] 是否会越界?

func Test_slice(t *testing.T){
    s := []int{0,1,2,3,4}
    s = append(s[:2],s[3:]...)
    t.Logf("s: %v, len: %d, cap: %d", s, len(s), cap(s))
    v := s[4] 
    // 是否会数组访问越界
}

1.12 问题12

  • • 初始化切片 s 长度和容量均为 512
  • • 在 s 的基础上追加 append 一个元素

请问经过上述操作后,切片s 的内容、长度以及容量分别是什么?

func Test_slice(t *testing.T){
    s := make([]int,512)  
    s = append(s,1)
    t.Logf("len of s: %d, cap of s: %d",len(s),cap(s))
}