golang slice 是 struct 类型,作为参数传递时要传地址

总结: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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值