You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言遍历切片时切片扩容,为何输出不符合预期?

Go切片for range遍历的疑问

问题场景

我使用for range遍历如下切片代码时,得到的输出为:0A,1M,2C,。

代码:

x := []string{"A", "B", "C"}

for i, s := range x {
    print(i, s, ",")
    x[i+1] = "M"
    x = append(x, "Z")
    x[i+1] = "Z"
}

为何输出是这样而非0A, 1Z, 2Z?查阅文档得知切片传参基于底层数组指针,那切片传给for range循环也是传指针吗?我猜测切片扩容后,print的(i,s)仍基于原底层数组,该猜测是否正确?

核心原因解析

1. for range遍历切片的本质:复制切片结构体

Go里的切片是包含指针(指向底层数组)、长度、容量的结构体。当用for range遍历切片时,会先复制一份这个切片结构体,后续遍历的是这份复制后的切片,而非原切片本身。

这意味着:

  • 遍历的索引i和值s,完全基于复制后的切片初始长度(这里是3)生成,循环只会执行3次,不会因为原切片append扩容而增加循环次数。
  • 每次循环的s,是对应索引位置元素的副本,而非引用。

2. 输出结果逐次拆解

第一次循环(i=0, s="A")

  • 先打印0A,
  • 执行x[i+1] = "M":此时原切片未扩容,直接修改原底层数组的索引1位置为"M"
  • 执行x = append(x, "Z"):原切片容量为3,append后长度变为4,触发扩容(string切片扩容后容量变为6),此时原切片指向了新的底层数组
  • 执行x[i+1] = "Z":修改的是新底层数组的索引1位置为"Z",但for range遍历用的是复制后的切片(仍指向原底层数组),所以不影响后续遍历的s

第二次循环(i=1, s="M")

  • 这里的s是从原底层数组的索引1位置取的(也就是之前修改的"M"),所以打印1M,
  • 执行x[i+1] = "M":修改新底层数组的索引2位置为"M"
  • 执行x = append(x, "Z"):新切片长度变为5,容量保持6不变
  • 执行x[i+1] = "Z":修改新底层数组的索引2位置为"Z",同样不影响原底层数组

第三次循环(i=2, s="C")

  • s来自原底层数组的索引2位置,值还是初始的"C",所以打印2C,
  • 此时i+1为3,修改的是新数组的索引3位置,原底层数组的索引2位置从未被改动,因此不影响当前的s

3. 你的猜测验证

你的猜测部分正确:for range遍历的是初始切片的复制体,它指向原底层数组,即使原切片后续扩容指向新数组,遍历过程中取的s仍来自原底层数组的元素。但要明确:for range不是传指针,而是复制了整个切片结构体,这才是问题的核心。


内容的提问来源于stack exchange,提问作者Тимур Имашев

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 11:42:06