make切片的第三个参数是cap,指定底层数组容量,影响初始内存分配大小;cap≥len,否则panic;预设cap可减少append时的扩容次数,但超cap仍会自动realloc。

make 创建切片时第三个参数到底预留了什么
它预留的是底层数组的长度,不是切片本身的 len,也不是“保证不扩容”的绝对承诺——只影响初始分配的数组大小,后续仍可能 realloc。
- 第三个参数是
cap,决定底层数组分配多大:比如make([]int, 0, 10)会直接申请能存 10 个int的连续内存 - 如果后续
append元素总数 ≤cap,不会触发新内存分配;超过就会重新 malloc 更大的数组并拷贝 - 注意:
cap可以小于len吗?不能,make([]T, len, cap)要求cap >= len,否则 panic:panic: make slice with negative capacity or length
什么时候必须设第三个参数
当你明确知道最终元素数量、且频繁 append 时,预设 cap 能避免多次底层数组复制,尤其在循环中构建切片最明显。
- 典型场景:从数据库查 1000 条记录,逐条
append到切片 → 用make([]Record, 0, 1000)比make([]Record, 0)少约 9 次内存分配(按 2 倍扩容策略估算) - 反例:读文件逐行解析,但行数完全未知 → 硬设
cap=10000可能浪费内存,不如让运行时动态扩 - 注意:如果一开始就用
make([]T, n)(两个参数),那len == cap == n,等价于预分配且填满零值;这不是“预留”,是“已占满”
常见误判:cap 预留了空间就一定不会 realloc
不会 realloc 的前提是:所有 append 累计新增元素数 ≤ 初始 cap。一旦超限,Go 运行时照常扩容,和没设 cap 时逻辑一致。
- 错误认知:“设了
cap=100,后面append101 次就 panic” → 不会 panic,只是第 101 次会触发扩容 - 扩容规则:若原
cap ,新 <code>cap = cap * 2;否则每次增加约 25%(具体看源码runtime.growslice) - 验证方法:用
unsafe.Sizeof或runtime.ReadMemStats看分配次数,或打印cap(s)观察变化
和 slice 字面量、new 的关键区别
make 是唯一能指定 cap 的方式;new 只分配零值指针,不构造切片头;字面量如 []int{1,2,3} 的 cap 等于 len,无法自定义。
立即学习“go语言免费学习笔记(深入)”;
-
new([]int)返回*[]int,解引用后是nil切片(len=0, cap=0, data=nil),不能直接append -
[]int{1,2,3}等价于make([]int, 3, 3),没法写成[]int{1,2,3} // cap=10 - 想复用底层数组?得靠
s[i:j:j]语法控制新切片的cap,和make的第三个参数无关
真正容易被忽略的是:cap 影响的是内存分配时机,不是切片行为边界;它不改变 append 的语义,也不阻止越界 panic —— 那是 len 的事。










