前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库Go slice len cap 区别
GoGo语言与并发

Go 切片的 len 和 cap 分别决定什么,reslice 时各自起什么约束?

切片的len限定当前可访问的元素数量,cap限定在不重新分配底层数组时能扩展的最大长度。reslice时若high超过cap会触发运行时panic,append则根据是否超cap决定是否迁移到新数组。

前端进阶之旅 · 一题精讲更新于 2026.09.05
Go#语言与并发#并发编程
先看核心答案
理解线索

切片头:指针、长度、容量

  1. len=元素个数决定当前可访问索引范围,越界即panic
  2. cap=容量上限决定reslice时high的最大值
  3. reslice约束high不能超过cap,low≤high

当len<cap时,用'a[:cap(a)]'可扩展长度;当len==cap时,任何high>cap都会panic。

核心回答

先记住这个答案

切片是包含指针、长度和容量的描述符。len给出实际元素个数,索引必须小于它;cap表示切片可从其首元素延伸到所属底层数组结尾的元素总数。进行a[low:high]这样的reslice时,high必须≤cap,否则panic;append时若len+新增数≤cap,则直接修改底层数组,否则Go分配更大数组并复制原元素,返回新切片,原切片仍指向旧数组。因此,len控制访问边界,cap控制内存重用边界。

  • len是现有元素数,cap是可扩展上限。
  • reslice的高索引不能超过cap。
  • append超过cap时分配新底层数组。

切片头三字段与索引范围

Go的切片在运行时是一个三字段结构:指向底层数组的指针、长度len和容量cap。len是切片中实际存储的元素数量,它是for range和索引操作的上界。调用cap则得到从该切片第一个元素起,底层数组中仍可连续使用的元素总数,该值通常不小于len。例如make([]int, 3, 5)会创建len=3, cap=5的切片,底层数组总长为5。

当代码执行s[i]时,运行时要求i >= 0 && i < len(s),否则抛出index out of range。而reslice表达式s[low:high]的约束是0 ≤ low ≤ high ≤ cap(s)——注意high的上限是cap而不是len,所以可以用s[:5]将上面例子的长度扩展到5,此时新增元素是底层数组中已有的内容。若写成s[:6],则因cap=5直接触发panic。

Read循环:cap限制单次读取上限,len记录已读数据

假设要从网络连接读取消息,预先分配一个容量为1024字节的缓冲区:buf := make([]byte, 0, 1024),此时len=0, cap=1024。每次循环先构造临时切片chunk := buf[len(buf):cap(buf)],其长度正好等于剩余可用空间cap-len。然后调用n, _ := conn.Read(chunk),由于chunk的长度就是可写入上限,Read最多写入这么多字节。随后执行buf = buf[:len(buf)+n],把本次读入的n字节并入buf,更新len而cap不变。

这种写法巧妙地同时利用了len和cap:cap提供了单次可读取的最大长度,len维护了当前有效数据边界。当len增长到cap(此处为1024)时,chunk的长度变为0,Read将不会写入任何数据。此时若消息尚未读完,就必须用append或重新申请更大缓冲区。这里的关键是,reslice时把high设为cap(buf)是合法的,因为它没有突破cap,否则代码会在运行时panic。

len==cap时的扩展困境与错误直觉

当切片的len等于cap时,表示底层数组剩余可用空间为零。此时任何试图通过reslice扩大长度的操作都会因为high>cap而panic,因为cap是硬上限。例如a := make([]int, 3, 3),执行a[:4]必panic,而a[:3]合法但长度不变。若继续追加数据,只能调用append(a, x),它会自动分配一个新底层数组(容量通常翻倍),原切片继续指向旧数组,之后对新切片赋值不会影响旧切片。

容易犯的错误是认为只要append没有超过cap就必然修改原切片。准确条件是:len + 所需增加数 ≤ cap时,append才会复用原数组,否则必然分配新数组。另一个误区是当len接近cap时,有人喜欢用if len(s) < cap(s) { s = s[:len(s)+1] }来手动扩容。这样虽能增加len,但未对新位置写入有价值的数据(可能留下零值),且若不检查cap会直接panic。更安全的做法是用append并接收返回值。

回答前,多想一步

容易答错的地方

认为append超过cap后原切片自动更新
许多初学者以为append总会原位追加,但超过cap后append会分配新底层数组并拷贝,仅返回的新切片指向新数组,旧切片仍指向原数组,二者数据独立。因此必须用slice = append(slice, x)的形式接收返回值。
误解reslice时high不能超过len
误解reslice时high不能超过len 切片表达式的high允许超过len,只要不超过cap。例如make([]int,3,5)后,a[:5]合法,得到新切片长度5,此时可以安全访问索引3、4。但在执行a[:5]之前,直接索引a[3]或a[4]会因当前len为3而越界;执行后则不再越界。
试着用自己的话回答

面试官还会怎么问?

append超过cap后新切片的容量如何增长?

Go规范不保证具体倍数。常见实现中,原容量小于1024时按2倍增长,大于等于1024时约按1.25倍增长,但不同版本可能调整。编程时不应依赖精确倍数,只依赖cap在新长度增加后足够容纳原有元素和新追加元素。

对nil切片执行reslice会panic吗?

不一定会panic。nil切片的len和cap均为0,切片表达式s[low:high]必须满足0 ≤ low ≤ high ≤ cap。例如s[0:0]合法,得到空切片;而任何low>0或high>0的表达式(如s[0:1]、s[1:])都会因越界而panic。nil切片可以调用append,它会自动分配底层数组并返回可用的新切片。判断切片是否为空应使用len(s)==0,而非s==nil。

如何安全地检查能否用reslice扩展而不越界?

扩展前先检查目标长度n是否满足n <= cap(s),例如if n <= cap(s) { newS := s[:n] }。扩展后新长度可以大于旧len,但新增部分必须位于底层数组有效范围内。如果还要写入具体值,可能触发扩容,更安全的做法是使用append并接受它返回的新切片。

从一道题,走向一组知识

把知识连起来

语言与并发

Go 的 nil 切片和空切片有什么区别,序列化和判空时各要注意什么?

nil切片与空切片的len/cap均为0,但行为不同,理解它有助于识别切片是否可安全扩展。

语言与并发

Go 对大切片取子切片后为什么会造成内存无法释放,如何避免?

reslice仅改变len和指针,cap可能保留整个大数组,与本主题的cap限制紧密相关。

参考资料

  • Effective Go

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 切片头三字段与索引范围
  3. Read循环:cap限制单次读取上限,len记录已读数据
  4. len==cap时的扩展困境与错误直觉
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑