slice作为参数时append需要注意的地方

最近在开发过程中遇到这样一个问题:一个函数接收slice作为参数,然后会append数据,但是append的数据对调用者不可见,具体看下面代码

func TestSlice(t *testing.T) {
	sli := make([]int, 0, 2)
	sli = append(sli, 1)
	sliceAppend(sli)
	fmt.Printf("%v\n", sli) //[0]
}

func sliceAppend(ans []int) {
	ans = append(ans, 2)//对调用者不可见
	ans[0] = 0//对调用者可见
}

这里为什么会输出[0],而不是[0, 2]呢?
要想搞清楚这个问题,先来看看slice底层结构

type slice struct {
	array unsafe.Pointer //底层数组指针
	len   int //长度
	cap   int //容量
}

go语言都是值传递,所以sliceAppend函数的入参ans其实是sli的副本,但是ans和sli是指向相同的底层数据的,所以ans[0] = 0会生效,但是为什么ans = append(ans, 2)会对TestSlice不可见呢?
因为slice是通过len来控制底层数组的可见范围的,调用完sliceAppend后sli的len并没有变,仍是1,所以是只能访问到0,不能访问2的。可以通过下面代码来加深理解

func TestSliceAddr(t *testing.T) {
	sli := make([]int, 0, 2)
	sli = append(sli, 1)
	fmt.Printf("sli[0] addr=:%p\n", &sli[0])
	sliceAppend(sli)
	fmt.Printf("%v\n", sli)
	header := *(*uintptr)(unsafe.Pointer(&sli))
	fmt.Printf("sli len = %v\n", len(sli))//1
	fmt.Printf("sli[0] = %v\n", *(*int32)(unsafe.Pointer(header + 0)))//0
	//强制访问sli[1]
	fmt.Printf("sli[1] = %v\n", *(*int32)(unsafe.Pointer(header + 8)))//2
}

func sliceAppend(ans []int) {
	fmt.Printf("befor reallomc ans[0] addr=:%p\n", &ans[0])//和sli共享底层数组,所以 &ans[0] == &sli[0]
	ans[0] = 0
	ans = append(ans, 2)
	fmt.Printf("befor reallomc ans[0] addr=:%p\n", &ans[0])//底层数组仍没有改变,所以 &ans[0] == &sli[0]
	ans = append(ans, 3)
	fmt.Printf("after reallomc ans[0] addr=:%p\n", &ans[0])//append 3之前,len == capacity,所以需要进行重分配,导致底层数组发生了变化,所以&ans[0] != &sli[0]

/**
output:
sli[0] addr=:0xc0000a61d0
befor reallomc ans[0] addr=:0xc0000a61d0
befor reallomc ans[0] addr=:0xc0000a61d0
befor reallomc ans[0] addr=:0xc0000a61d8
after reallomc ans[0] addr=:0xc0000da0a0
[0]
sli len = 1
sli[0] = 0
sli[1] = 2
*/
}

如果想要实现sliceAppend里面append数据对调用者可见可以使用下面的方式

func sliceAppend2(ans []int) []int{
	ans[0] = 0
	ans = append(ans, 2)
	ans = append(ans, 3)
	return ans
}

//传指针
func sliceAppend3(ans *[]int) {
	(*ans)[0] = 0
	*ans = append(*ans, 2)
	*ans = append(*ans, 3)
}

参考链接:
https://stackoverflow.com/questions/20195296/golang-append-an-item-to-a-slice

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值