https://victoriametrics.com/blog/go-slice/index.html

https://pkg.go.dev/unsafe#SliceData

slice 在传递给函数的过程中,如果没有指定内存地址传递,则传递的是 slice 的一个副本,
如果你希望改变 slice 的值,最好传递内存地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import "fmt"

func main() {
s := []int{1, 2, 3}
changeSlice(s)
fmt.Printf("s address: %p\n", &s)
fmt.Println("s is ", s)

fmt.Println("==change slice by reference==")

t := []int{1, 2, 3}
changeSliceByReference(&t)
fmt.Printf("t address: %p\n", &t)
fmt.Println("t is ", t)
}

func changeSlice(s []int) {
s[0] = 100
s = append(s, 200, 300)
fmt.Printf("s address: %p\n", &s)
}

func changeSliceByReference(slice *[]int) {
(*slice)[0] = 100
*slice = append(*slice, 200, 300)
fmt.Printf("t address: %p\n", slice)
}

/*
* output
* s address: 0xc000010030
* s address: 0xc000010018
* s is [100 2 3]
* ==change slice by reference==
* t address: 0xc000010060
* t address: 0xc000010060
* t is [100 2 3 200 300]
*/

利用go build命令可以查看变量是否逃逸到heap

1
2
3
4
5
6
7
8
9
10
11
12
package main

func main() {
a := [10 * 1024 * 1024]byte{}
println(&a)

b := [10*1024*1024 + 1]byte{}
println(&b)
}

// 执行命令 go build -gcflags="-m" slice_in_go.go
// output: ./slice_in_go.go:7:2: moved to heap: b

tips

  1. 初始化 slice 的时候最好预估数据量大小来设置 capacity,来避免频繁的创建新数组和复制数据。

  2. 修改 slice 最好传递引用,因为对于 int, string, array, slice 这些数据类型都是 passed-by-value 的形式传递参数的。

  3. In Go, everything is passed by value.

参考

there-is-no-pass-by-reference-in-go