插件窝 干货文章 Go语言append函数:为什么修改原切片会影响其副本?

Go语言append函数:为什么修改原切片会影响其副本?

切片 数组 底层 original 1002    来源:    2025-03-13

在Go语言中,切片(slice)是一个引用类型的数据结构,它包含了一个指向底层数组的指针、切片的长度和容量。当你使用append函数向切片添加元素时,如果切片的容量足够,append会在底层数组的末尾添加新元素,并返回更新后的切片。如果容量不足,append会创建一个新的底层数组,并将原数组的元素复制到新数组中,然后在新数组的末尾添加新元素。

为什么修改原切片会影响其副本?

  1. 共享底层数组:当你创建一个切片的副本时,副本和原切片共享同一个底层数组。这意味着如果你通过其中一个切片修改了底层数组的元素,另一个切片也会看到这些修改。

  2. append操作的影响

    • 容量足够时:如果切片的容量足够,append操作会在底层数组的末尾添加新元素,并返回更新后的切片。由于原切片和副本共享同一个底层数组,原切片的修改会反映在副本中。
    • 容量不足时:如果切片的容量不足,append操作会创建一个新的底层数组,并将原数组的元素复制到新数组中。此时,原切片和副本将不再共享同一个底层数组,因此原切片的修改不会影响副本。

示例代码

package main

import "fmt"

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

    // 修改原切片
    original[0] = 100
    fmt.Println("Original:", original) // 输出: Original: [100 2 3]
    fmt.Println("Copy:", copySlice)    // 输出: Copy: [100 2 3]

    // 使用append添加元素
    original = append(original, 4)
    fmt.Println("After append to original:", original) // 输出: After append to original: [100 2 3 4]
    fmt.Println("Copy after append:", copySlice)       // 输出: Copy after append: [100 2 3]

    // 再次修改原切片
    original[0] = 200
    fmt.Println("Original after modification:", original) // 输出: Original after modification: [200 2 3 4]
    fmt.Println("Copy after modification:", copySlice)    // 输出: Copy after modification: [100 2 3]
}

解释

  1. 初始状态originalcopySlice共享同一个底层数组。
  2. 修改原切片:修改original的第一个元素为100,copySlice也会看到这个修改。
  3. append操作:向original添加一个新元素4,由于容量不足,append创建了一个新的底层数组。此时,originalcopySlice不再共享同一个底层数组。
  4. 再次修改原切片:修改original的第一个元素为200,copySlice不会受到影响,因为它们不再共享同一个底层数组。

总结

  • 当切片的容量足够时,append操作不会创建新的底层数组,原切片和副本会共享同一个底层数组,因此修改原切片会影响副本。
  • 当切片的容量不足时,append操作会创建新的底层数组,原切片和副本将不再共享同一个底层数组,因此修改原切片不会影响副本。

理解这一点对于正确处理切片和避免意外的副作用非常重要。