cap函数只对切片和通道有效,返回底层数组可容纳最大元素数或通道缓冲区大小;对数组调用会编译报错,因数组长度编译期固定且cap无语义意义。

cap 函数在 Go 中只对切片([]T)和通道(chan T)有效,对数组([N]T)直接调用会编译报错:cannot call cap on array。它不返回“当前已用长度”,那是 len 的职责;它返回的是底层数组可容纳的最大元素数(对切片)或缓冲区大小(对 channel)。
切片的 cap 值怎么算出来的?
切片的 cap 不是静态写死的,而是由其创建方式决定的——本质是“从底层数组起始位置到数组末尾的元素个数”。
- 直接字面量创建:
s := []int{1,2,3}→cap(s) == 3(底层数组就是这 3 个元素) - 用
make指定容量:s := make([]int, 2, 5)→len(s)==2,cap(s)==5 - 切片再切片:
orig := make([]int, 5, 8),然后s := orig[1:3]→cap(s) == 7(因为底层数组总长 8,从索引 1 开始,还剩 7 个位置)
关键点:只要没发生扩容(即没触发 append 超出当前 cap),所有共享同一底层数组的切片,其 cap 值都反映该数组剩余可用空间,不是原始声明容量。
为什么对数组调用 cap 会报错?
数组长度在编译期固定,cap 对它没有语义意义。Go 明确禁止这种用法,避免混淆。
常见错误写法:
var a [5]int c := cap(a) // 编译错误:cannot call cap on array
如果真需要类似“数组容量”的值,只能硬写 5 或用 len(a)(对数组来说 len == cap,但语言不让你用 cap 去取)。
cap 在 append 和内存分配中的实际影响
cap 直接决定 append 是否触发扩容——这是性能关键点。
- 如果
len(s) ,<code>append复用底层数组,O(1) 时间 - 如果
len(s) == cap(s),append分配新底层数组(通常翻倍),拷贝旧数据,O(n) 时间
所以预估容量很重要。比如要追加 100 个元素,别用 make([]int, 0) 起手,改用 make([]int, 0, 100),避免多次 realloc。
注意:cap 不保证内存连续性以外的任何东西,也不影响 GC 行为——只要切片变量还存活,整个底层数组就受引用保护,哪怕你只用了前几个元素。
最容易被忽略的是切片再切片后 cap 的“隐式增长”:它让后续 append 可能意外覆盖原切片未导出的数据,调试时难定位。别只看 len,每次做 [:] 操作后,顺手打个 fmt.Println(len(s), cap(s)) 是好习惯。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











