len 和 cap 本质不同:len 是当前长度,cap 是从切片起始地址到底层数组末尾的可用长度;cap 随切片起始位置变化而动态计算,非继承固定值。

Go 切片的 len 和 cap 不是“差不多的概念”,它们在内存行为、扩容逻辑和共享底层数组时的表现截然不同——混淆二者,轻则导致意外数据覆盖,重则引发 goroutine 间静默数据竞争。
为什么 c[:2] 的 cap 还是 5,而 c[2:5] 的 cap 变成 3?
因为 cap 不是从原始切片“继承”来的固定值,而是从当前切片起始地址到底层数组物理末尾的距离。
-
c := b[:2]中,b底层数组长度为 5,c起始地址仍指向数组索引 0 →cap(c) == 5 -
d := c[2:5]实际等价于b[2:5],起始地址移到索引 2 → 剩余可用位置只有索引 2、3、4 →cap(d) == 3 - 这个计算公式是固定的:
cap(newSlice) = cap(oldSlice) - i(其中i是切片表达式左边界)
make([]int, 0, 5) 分配的底层数组,为什么一上来就是全 0?
不是切片“初始化了元素”,而是 Go 在分配底层数组时,按规范对所有未显式初始化的内存填充类型零值——int 的零值就是 0。
-
b := make([]int, 0, 5)分配的是一个长度为 5 的数组,只是切片视图长度为 0 -
c := b[:2]并没写入任何新值,只是把已存在的两个0暴露出来 - 所以
c[0] = 1会直接改写底层数组索引 0 的值,影响所有共享该数组的切片
用 append 扩容后,旧切片的 cap 会变吗?
不会。但旧切片和新切片很可能已不再共享底层数组——这是最易被忽略的隐性断裂点。
- 当
append触发扩容(如len(s) == cap(s)),Go 会分配新数组、复制数据、返回新切片头 - 原切片变量仍指向旧数组,
len和cap都不变,但修改它不再影响append后的结果 - 典型陷阱:把切片传给函数,在函数内
append后没接收返回值,以为原切片已增长
真正难的不是记住公式,而是意识到:每次切片操作都在悄悄移动指针;每个 append 都可能切断共享;cap 是个动态的、相对的、基于地址的度量——它不承诺空间,只承诺“从这里开始还能走多远”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











