
本文详解go切片的长度(len)与容量(cap)差异,阐明为何即使cap足够,访问b[2]仍会panic,并演示如何通过合法切片表达式扩展可索引范围。
在Go语言中,切片(slice)是一个引用类型,由三部分组成:指向底层数组的指针、长度(len)和容量(cap)。关键点在于:只有索引在 [0, len(s)) 范围内的元素才可通过下标直接访问;超出该范围的索引(即使仍在 [len(s), cap(s)) 内)属于“不可见但未释放”的内存空间,无法通过 s[i] 语法读写——这是语言规范强制的安全边界。
回到你的示例代码:
s := []byte{'A', 'W', 'T', 'Q', 'X'} // len=5, cap=5
b := s[2:4] // 取子切片:从索引2到4(不含4)
// 此时 b = []byte{'T', 'Q'}, len(b)=2, cap(b)=3(因底层数组剩余长度为5−2=3)虽然 cap(b) == 3,意味着底层数组在 b 起始位置之后还预留了3个字节(即 'T', 'Q', 'X'),但 b 的 当前长度仅为2,因此合法索引仅有 b[0] 和 b[1]。执行 b[2] = 'V' 时,Go运行时检测到索引 2 >= len(b),立即触发 panic: runtime error: index out of range。
✅ 正确做法是:先通过切片表达式显式扩展长度,再访问新索引。只要扩展后的新长度不超过 cap(b),就是安全且合法的:
立即学习“go语言免费学习笔记(深入)”;
b := s[2:4] // len=2, cap=3 b = b[:3] // ✅ 合法重切:将长度扩展至3(仍在cap范围内) b[2] = 'V' // 现在可安全赋值 fmt.Println(string(b)) // 输出 "TQV"
⚠️ 注意事项:
- b[:n] 是重设长度的操作,不分配新内存,仅修改切片头中的 len 字段;
- 若 n > cap(b)(如 b[:4]),则编译通过但运行时 panic;
- append 函数在容量足够时不扩容,本质也是隐式执行类似 b[:len+1] 的操作;
- 永远不要假设 cap 等价于“可用索引上限”——它只是“最大可扩展长度”的上限。
总结:Go切片的 len 定义当前逻辑视图大小,cap 定义底层数组可支撑的最大逻辑视图大小。索引安全由 len 保障,性能优化(避免分配)由 cap 支撑。二者协同实现高效而安全的动态数组抽象。










