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

Go中共享底层数组的两个切片append后结果不符预期原因求解

认知错误点

你对切片的核心认知偏差有三点:

  • Go的切片是值传递,不是引用传递:你把切片传给incr函数时,传的是切片结构体(由底层数组指针、长度len、容量cap三个字段组成)的完整副本。你在函数内部对副本的指针、长度、容量做的任何修改,都完全不会影响外部的原始切片。
  • 你误以为s2追加元素3时还和s1共享底层数组:字面量初始化的s1 := []int{1,2}长度和容量都是2,没有多余空间,给s2追加元素时会触发扩容,直接分配新的底层数组,此时s2和s1已经彻底解绑,后续修改互不影响。
  • 你误以为incr函数内的修改会同步到外部切片:incr接收s1的副本后,执行append时同样会因为容量不足触发扩容,所有修改都作用在新分配的底层数组上,外部s1完全感知不到。
代码实际运行逻辑

步骤1:初始化切片

s1 := []int{1, 2}
s2 := s1

此时s1和s2共享底层数组A[1,2],二者的结构体都是ptr=数组A地址, len=2, cap=2。

步骤2:给s2追加元素3

s2 = append(s2, 3)

s2当前长度等于容量,没有多余空间存储新元素,触发扩容:

  • 申请新的底层数组B,容量翻倍为4(Go切片小于1024字节时默认扩容策略为翻倍)
  • 将数组A的[1,2]复制到数组B的前两位,第三位赋值为3
  • 更新s2的结构体为ptr=数组B地址, len=3, cap=4
    此时s1仍然指向数组A,和s2再无关联。

步骤3:调用incr(s1)

将s1的副本传入incr函数,函数内的参数s初始结构体为ptr=数组A地址, len=2, cap=2:

  1. 执行s = append(s, 0):容量不足触发扩容,申请新的底层数组C,容量为4,复制数组A的[1,2]到C的前两位,第三位赋值为0,此时函数内的s结构体更新为ptr=数组C地址, len=3, cap=4
  2. 循环给s的所有元素自增,数组C变为[2,3,1]
  3. 函数执行结束,副本s被销毁,外部的s1没有任何变化,仍然指向数组A[1,2]。

步骤4:调用incr(s2)

将s2的副本传入incr函数,函数内的参数s初始结构体为ptr=数组B地址, len=3, cap=4:

  1. 执行s = append(s, 0):当前容量4足够容纳长度4的切片,不需要扩容,直接将数组B的第四位赋值为0,函数内的s结构体更新为ptr=数组B地址, len=4, cap=4
  2. 循环给s的所有元素自增,数组B从原来的[1,2,3,0]变为[2,3,4,1]
  3. 函数执行结束,副本s被销毁,外部的s2结构体仍然是ptr=数组B地址, len=3, cap=4,只能看到数组B的前3位[2,3,4]。

步骤5:打印结果

最终s1的值为[1,2],s2的值为[2,3,4],和代码注释的输出一致。

补充说明

只有满足以下两个条件时,函数内对切片元素的修改才会对外部可见:

  1. 函数内没有触发切片扩容,仍然和外部切片共享同一个底层数组
  2. 修改的元素在外部切片的长度范围内

内容的提问来源于stack exchange,提问作者unifreak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 23:30:00