len(s)返回当前可安全读写的元素个数,cap(s)返回从起始指针到底层数组末尾的元素总数;二者单位相同但语义不同,len用于逻辑长度判断,cap用于扩容和缓冲预估。

len() 返回你能用多少,cap() 返回你还能塞多少——不是“总大小”,也不是“预留空间”,而是两个完全不同的运行时字段。
切片里 len 和 cap 到底返回什么
对 []byte 或任意切片来说:
• len(s) 是当前可安全读写的元素个数(即逻辑长度)
• cap(s) 是从 s 起始指针位置开始、到底层数组末尾的元素总数(即扩展上限)
二者单位一致(比如 byte 就是字节),但语义完全不同。
常见错误现象:
• 把 cap(s) 当成“底层数组总长度”——错,它只算从当前起始地址往后数的部分
• 用 cap(s) - len(s) 算“剩余可用空间”来决定是否 append——危险,尤其在多次截取后,这个差值可能虚高或掩盖共享内存风险
示例:arr := [5]int{1,2,3,4,5}s := arr[1:3] → len(s) 是 2,cap(s) 是 4(不是 5,也不是 2)
立即学习“go语言免费学习笔记(深入)”;
什么时候该用 len(),什么时候该用 cap()
• 传参校验、序列化、写入 I/O:用 len(s)
• 预分配缓冲、判断是否需扩容、实现自定义 reader/writer:用 cap(s)
• 检查空切片:用 len(s) == 0,别用 s == nil(空切片不等于 nil)
容易踩的坑:
• 对通道调用 cap(ch) 合法,但对字符串调用会编译报错:invalid argument to cap
• map 和 chan 不支持 cap()(map 根本没容量概念)
• append(s, x) 后 cap(s) 可能远大于新 len(s),这不是 bug,是运行时为后续操作预留的空间
make 创建切片时三个参数怎么配
make([]T, len, cap) 中:
• 第二个参数是初始 len,第三个才是底层分配的 cap
• 省略第三个参数等价于 cap == len,不是“自动推导”
典型组合:
• make([]byte, 0, 4096):适合接收不定长数据,首次 append 不扩容
• make([]int, 10, 10):刚创建就满载,下一次 append 必触发 realloc
• make([]byte, 5, 1024):已有 5 字节有效数据,还能追加 1019 字节不换底层数组
性能影响:
• cap 过大会浪费内存;过小会导致频繁拷贝
• 截取操作如 s = s[2:] 会缩小 cap(起始指针偏移),但 s = s[:0] 不会动 cap
为什么 cap(s) 不等于底层数组总大小
因为 cap 的定义绑定的是切片头里的 Data 指针位置——它只管“从这儿开始往后还能用多少”,不管前面有没有被截掉的旧数据。
这意味着:
• 多个切片共享同一底层数组时,各自 cap 可能不同
• s = append([]byte(nil), s...) 是唯一能强制切断底层数组关联、让新 cap == len 的方式
• unsafe.Sizeof(s) 返回的是切片头结构体大小(通常 24 字节),和数据本身无关
最容易被忽略的一点:
即使 len(s) == cap(s),也不能断定“底层数组已满”——如果这个切片是从更大数组截取而来,真实底层数组可能仍有富余,只是当前视图看不到。


















