编程后端开发工程师

描述 Go 中 slices 的工作特性:什么是 nil slice,length 和 capacity 之间有什么区别,以及如何正确地增加 slice 而不出现数据泄漏或 panic?

用 Hintsage AI 助手通过面试

答案。

Slice 是动态数组,是在 Go 中处理数组的主要结构单位。历史上,编程语言提供了固定数组或更复杂的数据结构。Go 实现了 slices 作为处理内存集合的便捷工具,具有自动管理大小和修改长度的能力。

问题 在于如何正确管理内存,处理边界情况(空的、nil、满的 slices),以及理解 length 和 capacity 之间的区别。

解决方案 是正确使用内置函数 len()cap(),并按照 Go 的约定操作 slices。

代码示例:

var a []int // nil slice,len=0,cap=0 b := make([]int, 0) // 空 slice,len=0,cap=0 c := make([]int, 3, 5) // len=3,cap=5 c = append(c, 4, 5, 6) // cap 将自动增加

关键特性:

  • Nil slice (var s []int) 并不分配内存,内部与空 slice (make([]int, 0)) 仅有差异。
  • Length 显示当前元素数量,capacity 是不需要额外分配的最大值。
  • 在 append 时,如果 capacity 不足,底层会创建一个新的,而旧数据不变。

隐藏问题。

对 nil slice 进行 append 后,slice 的长度和容量会是多少?

Nil slice (var s []int) 在 append(s, 1) 后变为长度为 1,容量为 1 的 slice — Go 自动分配存储。

var s []int s = append(s, 42) // s 现在是 [42],len=1,cap=1

可以访问 nil slice 的 capacity 吗?

可以,nil slice 的两个函数 lencap 返回 0。不会有 panic。

var s []int fmt.Println(len(s), cap(s)) // 0 0

在没有预先为 nil slice 分配内存的情况下尝试按索引赋值时,会发生什么?

会产生 panic index out of range,因为 slice 没有元素。

var s []int s[0] = 1 // panic: runtime error: index out of range

常见错误和反模式

  • 在需要特定长度的函数中传递 nil slice 或空 slice 的错误。
  • 忽略 capacity 增长而重新填充 slice,编写不合理的带有 append 的循环。
  • 尝试通过索引赋值而不是使用 append 来添加元素。

生活中的示例

消极案例

团队决定在 Go 服务器中处处使用 var s []T,期望这总是等同于 make([]T, 0)。结果导致一些 JSON 序列化返回 null 而不是 []

优点:

  • 启动时更少的内存分配。

缺点:

  • JSON 工作不正常(结果返回 null 而不是数组)。
  • 通过索引访问时的运行时错误。

积极案例

使用 make([]T, 0, 100) 进行预分配,然后在循环中仅使用 append。这样可以最小化内存分配,并常常提高性能。

优点:

  • 高效的内存工作。
  • 在序列化时不会出现 panic 和意外值。

缺点:

  • 如果预期的元素数量与指定的 capacity 不符,可能会浪费内存。