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

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

切片 数组 slice2 底层 663    来源:    2025-03-13

在Go语言中,切片(slice)是一个引用类型,它包含指向底层数组的指针、切片的长度和容量。当你对一个切片进行append操作时,可能会影响其他切片,特别是当这些切片共享同一个底层数组时。

原因分析

  1. 共享底层数组

    • 当你创建一个切片时,它实际上是对一个底层数组的引用。如果你通过切片操作(如slice[start:end])创建了一个新的切片,这个新切片和原切片会共享同一个底层数组。
    • 如果你对其中一个切片进行修改(包括append操作),这些修改会反映在共享同一个底层数组的所有切片上。
  2. append操作的影响

    • 当你对一个切片进行append操作时,Go会检查切片的容量是否足够。如果容量足够,append会在底层数组的末尾添加新元素,并返回一个新的切片(长度增加,容量不变)。
    • 如果容量不足,append会创建一个新的底层数组,并将原数组的元素复制到新数组中,然后在新数组的末尾添加新元素。此时,原切片和新切片将不再共享同一个底层数组。

示例代码

package main

import "fmt"

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

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

    fmt.Println("Before append:")
    fmt.Println("slice1:", slice1) // [1 2 3 4 5]
    fmt.Println("slice2:", slice2) // [2 3]

    // 修改slice2
    slice2[0] = 99

    fmt.Println("After modifying slice2:")
    fmt.Println("slice1:", slice1) // [1 99 3 4 5]
    fmt.Println("slice2:", slice2) // [99 3]

    // 对slice2进行append操作
    slice2 = append(slice2, 100)

    fmt.Println("After append to slice2:")
    fmt.Println("slice1:", slice1) // [1 99 3 100 5]
    fmt.Println("slice2:", slice2) // [99 3 100]
}

解释

  • slice1slice2共享同一个底层数组。
  • 修改slice2的元素会影响到slice1,因为它们共享同一个底层数组。
  • slice2进行append操作时,由于slice2的容量足够,append操作会直接在底层数组上进行修改,因此slice1也会受到影响。

如何避免影响

如果你不希望一个切片的修改影响到另一个切片,可以使用copy函数或创建一个新的切片并复制元素:

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])

    fmt.Println("Before append:")
    fmt.Println("slice1:", slice1) // [1 2 3 4 5]
    fmt.Println("slice2:", slice2) // [2 3]

    // 修改slice2
    slice2[0] = 99

    fmt.Println("After modifying slice2:")
    fmt.Println("slice1:", slice1) // [1 2 3 4 5]
    fmt.Println("slice2:", slice2) // [99 3]

    // 对slice2进行append操作
    slice2 = append(slice2, 100)

    fmt.Println("After append to slice2:")
    fmt.Println("slice1:", slice1) // [1 2 3 4 5]
    fmt.Println("slice2:", slice2) // [99 3 100]
}

在这个例子中,slice2slice1的一个独立副本,修改slice2不会影响slice1

总结

  • 切片是引用类型,多个切片可能共享同一个底层数组。
  • 对共享底层数组的切片进行修改(包括append操作)可能会影响其他切片。
  • 如果需要独立操作切片,可以使用copy函数或创建新的切片并复制元素。