用 index 更轻量,value 会隐式拷贝大结构体导致 10%–30% 性能下降;数组 range 默认遍历索引而非元素,易误用,应显式用 arr[i] 或 _ , v := range arr。

range 遍历切片时用 value 还是 index?性能差多少?
差别不大,但用 value 会隐式拷贝元素,对大结构体或自定义类型有明显开销;用 index 则只取地址,更轻量。这不是“要不要用”的问题,而是“你知不知道自己在拷什么”。
- 基础类型(
int、string)拷贝成本低,range v := s和range i := s几乎没差异 - 结构体超过 16 字节(比如含多个字段的
struct{a,b,c,d int64}),value拷贝会触发内存复制,压测可见 10%–30% 性能下降 - 如果后续只读
v.field且不改v,编译器可能优化掉拷贝 —— 但别依赖,Go 1.21 前不总生效 - 安全起见,只要你不打算修改副本,就写
for i := range s,再用s[i]访问
数组遍历为什么不能像切片一样省略 index?
因为数组长度是类型的一部分,range 对数组做的是“值拷贝”,不是引用。你写 for v := range arr,v 是数组索引(等价于 0,1,2,...),不是元素值 —— 这和切片行为完全不同,极易误用。
- 错误写法:
var arr [3]int; for v := range arr { fmt.Println(v) }→ 输出0 1 2,不是arr[0] arr[1] arr[2] - 正确取值:必须显式写
for i := range arr { fmt.Println(arr[i]) }或for _, v := range arr { fmt.Println(v) } - 数组传参时默认整体拷贝,哪怕只遍历,也建议传指针:
func f(p *[3]int)避免无谓复制
for i := 0; i
几乎一样快,现代 Go 编译器对两者都做了边界检查消除和索引优化。但 range 更安全、更符合 Go 风格,除非你需要跳步、反向或动态控制下标。
-
range自动省略越界检查(已知长度),for i循环里每次s[i]默认仍有检查 —— 但编译器通常会合并/消除 - 若循环体里有
append或并发修改底层数组,len(s)可能变化,而range在开始时就固定了迭代次数,行为更可预测 - 需要下标运算(如
s[i+1])或条件跳过(continue后想跳两个)时,老式for不可替代
sync.Pool + 切片复用时,range 遍历要注意什么?
复用的切片可能残留旧数据,而 range s 只遍历 s.len 长度,不会管底层数组是否更大 —— 看似安全,实则容易漏逻辑或误读脏数据。
立即学习“go语言免费学习笔记(深入)”;
- 从
sync.PoolGet 出来的切片,务必重置长度:s = s[:0]或s = s[:n],否则range会遍历到上次遗留内容 - 不要依赖
cap(s)判断“可用空间”,range只看len;但如果你手动索引s[i]超出len,就直接 panic - Pool 中放切片指针(
*[]T)不如放结构体封装data []T并带Reset()方法,语义更清晰
实际写代码时,最常被忽略的是:数组的 range 返回索引而非值,且无法通过语法糖绕过 —— 这一点在从切片切换到数组时,几乎必然踩一次坑。











