Golang 切片的本质 & 切片表达式

切片的本质

切片,本质上就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)

示例:对于数组 a := [8]int{0, 1, 2, 3, 4, 5, 6, 7}

  • 切片 s1 := a[:5],相应示意图如下:


  • 切片 s2 := a[3:6],相应示意图如下:


切片表达式

切片表达式,从字符串、数组、指向数组或切片的指针构造子字符串或切片。它有两种变体:

  • 简单切片表达式:指定 low 和 high 两个索引界限值的简单的形式
  • 完整切片表达式:除了 low 和 high 索引界限值外,还指定容量的完整的形式

「 简单切片表达式 」

	// 切片的底层,就是一个数组
	// 所以,可以基于数组,通过切片表达式得到切片
	a := [5]int{1, 2, 3, 4, 5}
	// 简单切片表达式 s := a[low:high]
	// 其中,low 和 high,表示索引范围,左包含,右不包含,即:[low,height)
	s := a[1:3]
	fmt.Println(s)      // [2,3]
	fmt.Println(len(s)) // 2  // 得到的切片的长度为 high - low
	fmt.Println(cap(s)) // 4  // 从数组a中选出 1<= 索引值 < 4 的元素组成切片s

	// 简写方式:为了方便起见,可以省略切片表达式中的任何索引 — 省略了low,则默认为0;省略了high,则默认为切片操作数的长度
	a[2:]  // 等同于 a[2:len(a)]
	a[:3]  // 等同于 a[0:3]
	a[:]   // 等同于 a[0:len(a)]

需注意的是:

  • 对于数组或字符串,如果 0 <= low <= high <= len(a),则索引合法,否则,就会索引越界
  • 对切片,再执行切片表达式时( 切片再切片 ),high 的上限边界是切片的容量 cap(a),而不是长度
  • 常量索引,必须是非负的,并且,可以用 int 类型的值表示
    • 对于数组或常量字符串,常量索引,也必须在有效范围内
    • 如果 low 和 high 两个指标都是常数,必须满足 low <= high
    • 如果索引在运行时超出范围,就会发生运行时panic
	a := [5]int{1, 2, 3, 4, 5}
	
	// s := a[low:high]
	s := a[1:3]
	fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s)) // s:[2 3] len(s):2 cap(s):4
	// 索引的上限是cap(s),而不是len(s)
	s2 := s[3:4]
	fmt.Printf("s2:%v len(s2):%v cap(s2):%v\n", s2, len(s2), cap(s2)) // s2:[5] len(s2):1 cap(s2):1

完整切片表达式

对于数组,指向数组的指针,或切片 a,支持完整切片表达式;字符串不支持

	// 基本语法:a[low : high : max]
	a := [5]int{1, 2, 3, 4, 5}
	//(1)得到与简单切片表达式 a[low: high] 相同类型、相同长度和元素的切片
	//(2)将得到的结果切片的容量设置为 max-low
	t := a[1:3:5]
	fmt.Printf("t:%v len(t):%v cap(t):%v\n", t, len(t), cap(t)) // t:[2 3] len(t):2 cap(t):4

	// 说明:
	// 完整切片表达式中,只有第一个索引值(low)可以省略,默认为 0
	// 完整切片表达式需要满足的条件是0 <= low <= high <= max <= cap(a),其他条件和简单切片表达式相同