README.md 5.4 KB

第 05 课:数组与切片

学习目标

学会保存一组同类型数据,并使用上一课的循环遍历它们。按 main.go 中的 1~7 节逐段手敲,不急着学习复杂内存管理。

本课独立存放,共享根目录的 go.mod,仅使用标准库,无需安装第三方依赖。

1. 数组:固定长度

scores := [3]int{80, 90, 75}
fmt.Println(scores[0]) // 第一个元素,值为 80。
scores[2] = 85         // 修改第三个元素。
  • [3]int 表示包含 3 个整数的数组,长度也是类型的一部分;[3]int 与 [4]int 是不同类型。
  • 索引从 0 开始,最后一个元素的索引是 len(scores)-1。
  • var scores [3]int 不给初始值时,三个元素都为 0。
  • 数组长度不能在运行时改变,越界访问会导致编译错误或运行时 panic。

与 C 类似,可以通过索引访问数组;但 Go 会做边界检查,而且数组赋值会复制整个数组。

2. 用 range 遍历

for index, score := range scores {
    fmt.Println(index, score)
}

index 是索引,score 是当前元素值的副本。只需要值时,写 for _, score := range scores。

修改 score 不会直接修改数组中的整数元素。要修改原元素,应通过索引,例如 scores[index] = score + 1。

3. 切片:常用的序列类型

numbers := []int{10, 20, 30}
numbers = append(numbers, 40, 50)
对比 数组 切片
类型示例 [3]int []int
长度 固定,属于类型的一部分 运行时可变化,不属于类型的一部分
追加 不能直接 append 使用 append 并接收返回值
赋值 复制整个数组 复制切片描述信息,可能共享底层数组

append 返回更新后的切片值,要写回变量。底层空间不足时可能分配新的数组,不要假设追加后原来的共享关系始终不变,也不要依赖固定的容量增长倍数。

4. 截取与共享

middle := numbers[1:3]

范围是左闭右开:包含索引 1、2,不包含 3;因此长度是 3-1=2。

常见写法还有 numbers[:2](前两个元素)、numbers[2:](从索引 2 到末尾)、numbers[:](全部)。

截取得到的子切片不是独立副本,它与原切片共享底层数组。因此本课 middle[0] = 200 会修改 numbers[1]。

alias := numbers 也不是复制全部元素;修改 alias 中已有元素,可能影响 numbers。

5. 独立复制

backup := make([]int, len(numbers))
copied := copy(backup, numbers)

make 创建目标切片,copy 复制元素并返回实际复制数量,即两边长度的较小值。目标长度不够时,不会自动扩容。

本课元素是整数,所以这样能得到不共享元素存储的独立副本;将来元素包含切片或指针时,还需要理解浅拷贝。

6. 零值、长度与容量

  • var tasks []string 的零值为 nil,可以直接 append。
  • len 是当前可访问的元素数量。
  • cap 是从切片起点到底层数组末尾的容量,不是可直接使用索引访问的范围。
  • 即使 cap 大于 len,也不能直接访问 slice[len(slice)],应先 append 或合法地重新切片。
  • 空的非 nil 切片也存在,例如 []int{};长度为 0 并不一定表示 nil。

编译并运行

在仓库根目录执行:

New-Item -ItemType Directory -Force bin/05_arrays_slices | Out-Null
go build -o ./bin/05_arrays_slices/arrays_slices.exe ./lessons/05_arrays_slices
.\bin\05_arrays_slices\arrays_slices.exe

修改源码后需要重新编译。快速编译并运行也可以用:

go run ./lessons/05_arrays_slices

或者从根目录进入本课,再执行 go run .:

Set-Location lessons/05_arrays_slices
go run .

预期输出

=== 数组 ===
成绩:[80 90 75],长度:3
第一个成绩:80
修改后的成绩:[80 90 85]

=== 遍历与求和 ===
第 1 个成绩:80
第 2 个成绩:90
第 3 个成绩:85
总分:255,平均分:85.00

=== 切片与追加 ===
切片:[10 20 30 40 50],长度:5

=== 截取与共享 ===
numbers[1:3]:[20 30]
修改子切片后,原切片:[10 200 30 40 50]

=== 复制切片 ===
复制了 5 个元素,副本:[999 200 30 40 50]
原切片保持不变:[10 200 30 40 50]

=== 从空切片开始 ===
追加前:nil=true,长度=0,容量=0
任务:复习循环
任务:学习切片

=== 数组赋值是复制 ===
数组副本:[100 90 85],原数组:[80 90 85]

动手练习

  • 修改三个成绩,重新核对总分和平均分。
  • 给 numbers 再追加两个整数,观察长度变化。
  • 改为截取 numbers[:2],预测输出后再运行。
  • 观察修改子切片与修改独立副本,对原切片的影响有何不同。
  • 为 tasks 追加第三个任务。
  • 在求和循环中结合 if score >= 90,统计优秀成绩数量。

特别注意:本例数组非空,可以计算平均值;未来处理空切片时,应先判断长度,避免除以零。

如果另外手敲一份,放进本课的 practice/ 子目录,用 go run ./lessons/05_arrays_slices/practice 运行,不要在同一个包中声明两个 main 函数。

下一课建议学 map,认识按“键”查找数据的方式。

上一课:for 循环 · 学习总览