go学习 - append

内置函数(build-in function) append 是向 slice 添加元素,用法:

var x []int
x = append(x, 1)
x = append(x, 2, 3)
x = append(x, 4, 5, 6)
x = append(x, x...) // append the slice x
fmt.Println(x) // "[1 2 3 4 5 6 1 2 3 4 5 6]"

熟悉 append 内存空间分配策略,有助于编写效率更高的代码。考虑 slice 的结构类型 { pointer, len, cap }

slice 指向一段空间,当调用 append 后,cap容量不够,类似于C++的vector的空间分配机制,重新申请一块新的内存空间,容量cap为原来的2倍。

测试代码:

package main

import (
    "fmt"
)

func main() {
    var res1 = make([]int, 0, 2)
    res1 = append(res1, 1)
    var res2 = res1

    fmt.Println("***append-1***")
    res1 = append(res1, 2)
    res2[0] = 0
    fmt.Println("res1:", res1, "&:", &res1[0], "cap:", cap(res1))
    fmt.Println("res2:", res2, "&:", &res2[0], "cap:", cap(res2))

    fmt.Println("***append-2***")
    res1 = append(res1, 3)
    res2[0] = 1
    fmt.Println("res1:", res1, "&:", &res1[0], "cap:", cap(res1))
    fmt.Println("res2:", res2, "&:", &res2[0], "cap:", cap(res2))

    fmt.Println("***append-3***")
    res1 = append(res1, 4, 5)
    fmt.Println("res1:", res1, "&:", &res1[0], "cap:", cap(res1))

    fmt.Println("***append-4***")
    res1 = append(res1, 6, 7, 8, 9)
    fmt.Println("res1:", res1, "&:", &res1[0], "cap:", cap(res1))

    // res1 = append(res1, 4, 5, 6, 7, 8, 9)
    // fmt.Println("res1:", res1, "&:", &res1[0], "cap:", cap(res1))

    fmt.Println("***append-5***")
    arr := [4]int{1, 2, 3, 4}
    sa := arr[2:]
    fmt.Println("arr:", arr, "&:", &arr[0], "cap:", cap(arr))
    fmt.Println("sa:", sa, "&:", &sa[0], "cap:", cap(sa))
    sa = append(sa, 5, 6)
    fmt.Println("arr:", arr, "&:", &arr[0], "cap:", cap(arr))
    fmt.Println("sa:", sa, "&:", &sa[0], "cap:", cap(sa))
}

output:
***append-1***
res1: [0 2] &: 0xc0420401c0 cap: 2       -> 修改res2[0]=1,有res1[0]=res2[0]=1
res2: [0] &: 0xc0420401c0 cap: 2
***append-2***
res1: [0 2 3] &: 0xc0420464c0 cap: 4     -> 重新申请了一块新的内存空间,容量为4
res2: [1] &: 0xc0420401c0 cap: 2         -> 取&res1[0],res1已经指向新的空间;修改res2[0],res1[0]没有变
***append-3***
res1: [0 2 3 4 5] &: 0xc0420480c0 cap: 8
***append-4***
res1: [0 2 3 4 5 6 7 8 9] &: 0xc04205a180 cap: 16
***append-5***
arr: [1 2 3 4] &: 0xc0420465c0 cap: 4
sa: [3 4] &: 0xc0420465d0 cap: 2
arr: [1 2 3 4] &: 0xc0420465c0 cap: 4
sa: [3 4 5 6] &: 0xc042046620 cap: 4	 -> sa指向新地址

测试中,出现了一个问题,现在还没找到答案。当 append 函数一次添加多个元素时,分配的内存空间可能出现不是2倍情况,应该是与 append 函数在实现添加多个元素时的策略有关。看代码:

package main

import (
  "fmt"
)

func main() {
  var res1 = make([]int, 0, 2)
  res1 = append(res1, 1)
  fmt.Println("res1:", res1, "&:", &res1[0], "cap:", cap(res1))     -> res1: [1] &: 0xc04200c250 cap: 2

  fmt.Println("***append-1***")
  res1 = append(res1, 2)
  fmt.Println("res1:", res1, "&:", &res1[0], "cap:", cap(res1))     -> res1: [1 2] &: 0xc04200c250 cap: 2

  fmt.Println("***append-2***")
  res1 = append(res1, 3)
  fmt.Println("res1:", res1, "&:", &res1[0], "cap:", cap(res1))     -> res1: [1 2 3] &: 0xc042002840 cap: 4

  fmt.Println("***append-???***")
  res1 = append(res1, 4, 5, 6, 7, 8, 9)
  fmt.Println("res1:", res1, "&:", &res1[0], "cap:", cap(res1))     -> res1: [1 2 3 4 5 6 7 8 9] &: 0xc04200e320 cap: 10 ???
}

对话与讨论