插件窝 干货文章 Go语言切片append操作:为什么修改一个切片会影响另一个?

Go语言切片append操作:为什么修改一个切片会影响另一个?

切片 数组 slice2 底层 537    来源:    2025-03-14

在Go语言中,切片(slice)是一个引用类型,它包含了一个指向底层数组的指针、切片的长度和容量。当你对切片进行append操作时,可能会遇到修改一个切片影响另一个切片的情况。这种情况通常发生在两个切片共享同一个底层数组时。

原因分析

  1. 共享底层数组

    • 当你从一个切片创建另一个切片时(例如通过切片操作slice[start:end]),新切片和原切片会共享同一个底层数组。
    • 如果你对其中一个切片进行修改(例如通过append操作),并且这个操作没有触发底层数组的重新分配,那么另一个切片也会受到影响,因为它们共享同一个底层数组。
  2. append操作的底层数组重新分配

    • append操作导致切片的长度超过其容量时,Go会重新分配一个新的底层数组,并将原数组的内容复制到新数组中。此时,新切片和原切片将不再共享同一个底层数组。
    • 如果append操作没有触发重新分配,那么新切片和原切片仍然共享同一个底层数组,修改一个切片会影响另一个。

示例代码

package main

import "fmt"

func main() {
    // 创建一个切片
    slice1 := []int{1, 2, 3, 4, 5}

    // 创建一个新切片,共享同一个底层数组
    slice2 := slice1[1:3]

    // 修改slice2
    slice2[0] = 99

    // 输出slice1和slice2
    fmt.Println("slice1:", slice1) // 输出: slice1: [1 99 3 4 5]
    fmt.Println("slice2:", slice2) // 输出: slice2: [99 3]

    // 对slice2进行append操作,未触发重新分配
    slice2 = append(slice2, 100)

    // 输出slice1和slice2
    fmt.Println("slice1:", slice1) // 输出: slice1: [1 99 3 100 5]
    fmt.Println("slice2:", slice2) // 输出: slice2: [99 3 100]

    // 对slice2进行append操作,触发重新分配
    slice2 = append(slice2, 200, 300)

    // 输出slice1和slice2
    fmt.Println("slice1:", slice1) // 输出: slice1: [1 99 3 100 5]
    fmt.Println("slice2:", slice2) // 输出: slice2: [99 3 100 200 300]
}

解决方案

如果你不希望两个切片共享同一个底层数组,可以通过copy函数或append操作来创建一个新的切片,确保它们使用不同的底层数组。

package main

import "fmt"

func main() {
    // 创建一个切片
    slice1 := []int{1, 2, 3, 4, 5}

    // 创建一个新切片,不共享同一个底层数组
    slice2 := make([]int, len(slice1[1:3]))
    copy(slice2, slice1[1:3])

    // 修改slice2
    slice2[0] = 99

    // 输出slice1和slice2
    fmt.Println("slice1:", slice1) // 输出: slice1: [1 2 3 4 5]
    fmt.Println("slice2:", slice2) // 输出: slice2: [99 3]
}

总结

  • 切片是引用类型,多个切片可能共享同一个底层数组。
  • append操作可能会触发底层数组的重新分配,导致切片不再共享同一个底层数组。
  • 如果你不希望切片之间相互影响,可以使用copy函数或append操作来创建新的切片,确保它们使用不同的底层数组。