总结:slice 为结构体类型,go 传参都是值传递,跨函数传递 slice 时,要传变量的地址,不能直接传递变量值,因为除了 len 会变,遇到扩容时,还会新建底层数组。
先看下 slice 的定义
type slice struct {
array unsafe.Pointer
len int
cap int
}
slice 是一个 struct,包含 array,len,cap 三个属性,其中 array 为 unsafe.Pointer 类型,且是 struct 的第一个属性,所以 array 的地址即为该 struct 的地址
dat := make([]int, 6)
上面的代码定义了一个 []int 类型的变量 dat,dat 是一个引用类型,指向底层数组的地址,其实我们要把 dat 看成一个 struct 类型,因为 len 和 cap 能影响 dat,看下面的代码
import (
"fmt"
"testing"
)
func fnA() {
dat0 := make([]int,6, 10)
dat := dat0[0:5]
dat[0] = 111
fmt.Printf("at fnA before fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
fmt.Printf("at fnA before fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
fmt.Printf("at fnA before fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
fnB(dat)
fmt.Printf("at fnA after fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
fmt.Printf("at fnA after fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
fmt.Printf("at fnA after fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
}
func fnB(a []int) {
fmt.Printf("at fnb before update a : %v, len(a) = %d \n", a, len(a))
a[0]=200
fmt.Printf("at fnb after update a : %v, len(a) = %d \n", a, len(a))
fmt.Printf("at fnb before append a addr is %p, var addr %p \n", a, &a)
a = append(a, 120)
fmt.Printf("at fnb after append a addr is %p, var addr %p \n", a, &a)
fmt.Printf("at fnb after append a : %v, len(a) = %d \n", a, len(a))
}
func TestPermute(t *testing.T){
fnA()
}
=== RUN TestPermute
at fnA before fnB(dat) dat value is [111 0 0 0 0], len(dat) = 5
at fnA before fnB(dat) dat0 value is [111 0 0 0 0 0], len(dat) = 6
at fnA before fnB(dat) dat addr is 0xc0000220a0, var addr 0xc00000c0c0
at fnb before update a : [111 0 0 0 0], len(a) = 5
at fnb after update a : [200 0 0 0 0], len(a) = 5
at fnb before append a addr is 0xc0000220a0, var addr 0xc00000c140
at fnb after append a addr is 0xc0000220a0, var addr 0xc00000c140
at fnb after append a : [200 0 0 0 0 120], len(a) = 6
at fnA after fnB(dat) dat addr is 0xc0000220a0, var addr 0xc00000c0c0
at fnA after fnB(dat) dat value is [200 0 0 0 0], len(dat) = 5
at fnA after fnB(dat) dat0 value is [200 0 0 0 0 120], len(dat) = 6
--- PASS: TestPermute (0.00s)
PASS
由于 golang 是值传递,所以 fnB(dat) 会创建一个新的 struct,这个可以从 &dat 与 &a 的值不同,以及 a 的 len 也没有传递给 dat 看出来。如果把 dat0 := make([]int,6, 10) 改为 dat0 := make([]int,0),可以看出底层数组也会改变
import (
"fmt"
"testing"
)
func fnA() {
//dat0 := make([]int,6, 10)
//dat0 := make([]int,0)
//dat := dat0[0:5]
dat := make([]int,0)
//dat[0] = 111
fmt.Printf("at fnA before fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
//fmt.Printf("at fnA before fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
fmt.Printf("at fnA before fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
fnB(dat)
fmt.Printf("at fnA after fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
fmt.Printf("at fnA after fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
//fmt.Printf("at fnA after fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
}
func fnB(a []int) {
fmt.Printf("at fnb before update a : %v, len(a) = %d \n", a, len(a))
//a[0]=200
fmt.Printf("at fnb after update a : %v, len(a) = %d \n", a, len(a))
fmt.Printf("at fnb before append a addr is %p, var addr %p \n", a, &a)
a = append(a, 120)
fmt.Printf("at fnb after append a addr is %p, var addr %p \n", a, &a)
fmt.Printf("at fnb after append a : %v, len(a) = %d \n", a, len(a))
}
func TestPermute(t *testing.T){
fnA()
}
=== RUN TestPermute
at fnA before fnB(dat) dat value is [], len(dat) = 0
at fnA before fnB(dat) dat addr is 0x6520a8, var addr 0xc00000c0c0
at fnb before update a : [], len(a) = 0
at fnb after update a : [], len(a) = 0
at fnb before append a addr is 0x6520a8, var addr 0xc00000c120
at fnb after append a addr is 0xc0000202e8, var addr 0xc00000c120
at fnb after append a : [120], len(a) = 1
at fnA after fnB(dat) dat addr is 0x6520a8, var addr 0xc00000c0c0
at fnA after fnB(dat) dat value is [], len(dat) = 0
--- PASS: TestPermute (0.00s)
PASS
Process finished with exit code 0
换成传地址就能返回想要的结果
import (
"fmt"
"testing"
)
func fnA() {
//dat0 := make([]int,6, 10)
//dat0 := make([]int,0)
//dat := dat0[0:5]
dat := make([]int,0)
//dat[0] = 111
fmt.Printf("at fnA before fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
//fmt.Printf("at fnA before fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
fmt.Printf("at fnA before fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
fnB(&dat)
fmt.Printf("at fnA after fnB(dat) dat addr is %p, var addr %p \n", dat, &dat)
fmt.Printf("at fnA after fnB(dat) dat value is %v, len(dat) = %d \n", dat, len(dat))
//fmt.Printf("at fnA after fnB(dat) dat0 value is %v, len(dat) = %d \n", dat0, len(dat0))
}
func fnB(a *[]int) {
fmt.Printf("at fnb before update a : %v, len(a) = %d \n", *a, len(*a))
//a[0]=200
fmt.Printf("at fnb after update a : %v, len(a) = %d \n", *a, len(*a))
//fmt.Printf("at fnb before append a addr is %p, var addr %p \n", a, &a)
fmt.Printf("at fnb before append a addr is %p, var addr %p \n", *a, &a)
*a = append(*a, 120)
//fmt.Printf("at fnb after append a addr is %p, var addr %p \n", a, &a)
fmt.Printf("at fnb after append a addr is %p, var addr %p \n", *a, &a)
fmt.Printf("at fnb after append a : %v, len(a) = %d \n", *a, len(*a))
}
func TestPermute(t *testing.T){
fnA()
}
=== RUN TestPermute
at fnA before fnB(dat) dat value is [], len(dat) = 0
at fnA before fnB(dat) dat addr is 0x6520a8, var addr 0xc00000c0c0
at fnb before update a : [], len(a) = 0
at fnb after update a : [], len(a) = 0
at fnb before append a addr is 0x6520a8, var addr 0xc00000e038
at fnb after append a addr is 0xc0000202e8, var addr 0xc00000e038
at fnb after append a : [120], len(a) = 1
at fnA after fnB(dat) dat addr is 0xc0000202e8, var addr 0xc00000c0c0
at fnA after fnB(dat) dat value is [120], len(dat) = 1
--- PASS: TestPermute (0.00s)
PASS
Process finished with exit code 0