Go语言切片append操作疑惑:为何修改y有时会影响x?
Go切片append操作的底层逻辑解析
要搞懂这几个示例的差异,核心得先明白Go切片的本质:每个切片都绑定了一个底层数组,切片自身只存三个值:当前长度(len)、最大容量(cap)、指向底层数组的指针。append操作的行为完全取决于「新增元素后是否超过切片容量」:
第一个示例:未触发扩容,共享底层数组
先看代码:
x := make([]int, 0, 4) x = append(x, 1, 2, 3, 4) // len(x)=4,cap(x)=4 y := x[:2] // len(y)=2,cap(y)=4(切片容量从起始索引到底层数组末尾) y = append(y, 30, 40) fmt.Println("x:", x) // 输出 [1 2 30 40] fmt.Println("y:", y) // 输出 [1 2 30 40]
- y的初始len是2,append两个元素后,len变成4,刚好等于它的cap(4),不需要扩容。
- 这时候append直接在共享的底层数组上修改:把数组索引2、3位置的原有值(3、4)替换成30、40。
- x的len是4,刚好覆盖整个底层数组,所以x输出的就是修改后的数组内容。
第二个示例:触发扩容,生成新底层数组
代码:
x := make([]int, 0, 4) x = append(x, 1, 2, 3, 4) // len(x)=4,cap(x)=4 y := x[:2] // len(y)=2,cap(y)=4 y = append(y, 30, 40,50) fmt.Println("x:", x) // 输出 [1 2 3 4] fmt.Println("y:", y) // 输出 [1 2 30 40 50]
- y初始len是2,append三个元素后,len变成5,超过了cap(4),触发自动扩容。
- Go会分配一个新的底层数组(容量通常是原容量的2倍,这里原cap是4,新容量一般为8),把y原有的元素(1、2)和新增元素(30、40、50)复制到新数组里。
- 此时y指向新数组,而x仍然指向原来的旧数组,所以x的内容不受影响,还是原来的[1,2,3,4]。
第三个示例:容量足够但原切片长度未覆盖修改位置
代码:
x := make([]int, 0, 5) x = append(x, 1, 2, 3, 4) // len(x)=4,cap(x)=5 y := x[:2] // len(y)=2,cap(y)=5 y = append(y, 30, 40, 50) fmt.Println("x:", x) // 输出 [1 2 30 40] fmt.Println("y:", y) // 输出 [1 2 30 40 50]
- y的cap是5,append三个元素后len变成5,刚好等于cap,不需要扩容,直接在共享底层数组上修改:索引2、3、4位置分别写入30、40、50。
- x的len是4,它只「看得见」底层数组的前4个元素(索引0-3),索引4的50不在x的长度范围内,所以x输出时不会显示这个值;而y的len是5,覆盖了整个数组,所以能看到全部5个元素。
内容的提问来源于stack exchange,提问作者greedsin
相关产品推荐
相关产品推荐

