首页 / Go 语言入门教程 / 切片操作

Go 语言入门教程

切片操作

本教程共 80 篇 · 第 29 篇 · 更新于 2026-07-27 · 约 8 分钟阅读

GoGo 入门教程切片Sliceappendcopy切片表达式

29. 切片操作

本节目标:学会用 append 往切片加元素,用切片表达式截取片段,用 copy 复制切片,以及怎么删掉不需要的元素。

append:往切片加东西

append 是切片最常用的操作,往尾部追加元素:

package main

import "fmt"

func main() {
    s := []int{1, 2, 3}
    s = append(s, 4)
    fmt.Println(s) // [1 2 3 4]

    // 一次加多个
    s = append(s, 5, 6, 7)
    fmt.Println(s) // [1 2 3 4 5 6 7]

    // 追加另一个切片(用...展开)
    s2 := []int{8, 9}
    s = append(s, s2...)
    fmt.Println(s) // [1 2 3 4 5 6 7 8 9]
}

注意 append 的返回值必须接住。为什么?因为切片可能需要扩容,扩容后会分配新的底层数组,原来的切片指针就失效了。

// 错误写法
append(s, 4) // s 没变!返回值丢了

// 正确写法
s = append(s, 4)
Warning

append 的第一个参数可能是 nil 切片,这完全没问题。append 会自动创建底层数组。

切片表达式:截取片段

s[low:high] 可以截取切片的一部分,和从数组创建切片的语法一样:

s := []int{1, 2, 3, 4, 5}

fmt.Println(s[1:3])  // [2 3]   下标1到3(不含3)
fmt.Println(s[:3])   // [1 2 3] 从头到下标3
fmt.Println(s[2:])   // [3 4 5] 从下标2到末尾
fmt.Println(s[:])    // [1 2 3 4 5] 全部

这种切片操作和原切片共享底层数组。改了新的,旧的也会变:

s := []int{1, 2, 3, 4, 5}
sub := s[1:3]

sub[0] = 100
fmt.Println(s)   // [1 100 3 4 5]  s 也变了!
fmt.Println(sub) // [100 3]

三索引切片表达式

Go 1.2 引入了三索引切片表达式 s[low:high:max],用来限制新切片的容量:

s := []int{1, 2, 3, 4, 5}

sub := s[1:3:4]
fmt.Println(sub)       // [2 3]
fmt.Println(len(sub))  // 2
fmt.Println(cap(sub))  // 3  (max - low = 4 - 1 = 3)

第三个参数 max 限制了新切片的容量上限。这样做的好处是:如果你不希望新切片通过 append 影响到原切片后面的元素,用三索引就能切断这个关联。

Note

三索引表达式必须满足 low <= high <= max <= cap(s),否则会 panic。

copy:复制切片

如果你想要一个完全独立的副本,用 copy

package main

import "fmt"

func main() {
    src := []int{1, 2, 3}
    dst := make([]int, 3)

    n := copy(dst, src)
    fmt.Println(dst)  // [1 2 3]
    fmt.Println(n)    // 3  返回复制的元素个数

    // 改副本不影响原件
    dst[0] = 100
    fmt.Println(src)  // [1 2 3]
}

copy 返回实际复制的元素个数,取 src 和 dst 长度的较小值。

Tip

copy 也可以用来在同一个切片内部搬数据,比如把后面的元素往前挪。src 和 dst 可以重叠。

删除元素

Go 的切片没有内置的 remove 方法。删除元素需要你自己用 append 来做。

删除下标 i 的元素,思路是:把 i 后面的元素往前挪一位,然后截掉最后一个:

package main

import "fmt"

func main() {
    s := []int{1, 2, 3, 4, 5}
    i := 2 // 要删除下标2的元素(值是3)

    s = append(s[:i], s[i+1:]...)
    fmt.Println(s) // [1 2 4 5]
}

拆开看就是三步:

  1. s[:i] 取删除点前面的部分:[1 2]
  2. s[i+1:]... 把删除点后面的部分展开追加进去:[4 5]
  3. append 把两部分拼起来就是 [1 2 4 5]
Warning

这种删除方式有个问题:如果切片里有指针或包含指针的结构体,被「删除」的元素其实还在底层数组里,可能导致内存泄漏。更安全的做法是先显式置 nil:

s := []*int{...}
i := 2
s[i] = nil              // 先释放引用
s = append(s[:i], s[i+1:]...)

在中间插入元素

append 只能往尾部加。要在中间插入,也是手动拼:

package main

import "fmt"

func main() {
    s := []int{1, 2, 4, 5}
    i := 2 // 在下标2处插入

    // 先追加一个空位
    s = append(s, 0)
    // 把 i 及之后的元素往后挪一位
    copy(s[i+1:], s[i:])
    // 在 i 处放入新值
    s[i] = 3

    fmt.Println(s) // [1 2 3 4 5]
}

虽然步骤多了点,但逻辑很清晰。

小结

切片操作的核心就四个:

  • append 加元素,返回值必须接住
  • s[low:high] 截取片段,注意共享底层数组
  • copy 做独立复制
  • 删除靠 append + 切片表达式手动拼

这些操作写多了就熟练了。下一章讲切片扩容的内部机制,搞懂 cap 为什么会「跳跃式增长」。