goland 的切片踩坑

问题

运行如下代码

package main

import (
	"fmt"
)

func AddElement(slice []int, e int) []int {
	return append(slice, e)
}

func main() {
	var slice []int
	slice = append(slice, 1, 2, 3)
	newSlice := AddElement(slice, 4)
	slice = append(slice, 5)
	slice = append(slice, 6)
	fmt.Println(newSlice)
}

运行结果:
go version 1.14

[1 2 3 5]

go version 1.16

[1 2 3 4]

很奇怪对不对???

于是乎我试了下这样子——

func main() {
	var slice []int
	slice = append(slice, 1, 2, 3, 4, 5, 6, 7)
	newSlice := AddElement(slice, 4)
	slice = append(slice, 5)
	slice = append(slice, 6)
	fmt.Println(newSlice)
}

这个时候两个版本的go输出的结果就都一样了

[1 2 3 4 5 6 7 5]

经过对比发现:
第一,出现这种append数据丢失的情况,往往发生在切片的 cap - len = 1的情况下,也就是说还有容量中最后一个位置可以放置元素的时候,新定义的切片进行append之后,老切片也append了,那么新定义的切片append的就会丢失。
第二,go 1.16和go 1.14在切片分配容量cap的时候,似乎有点区别,但是我看源码src/runtime/slice.go这部分的扩容机制都是一样的啊,不明白输出结果为啥不一样(求大佬明示)。

原因分析

切片是引用类型,是引用传递,newSlice 和 slice 底层用的都是同一个数组,所以newSlice进行了append之后,slice再进行append,等于修改了这个位置的元素。如下验证:可见第四个位置的地址没有变。

func main() {
	var slice []int

	slice = append(slice, 1, 2, 3)
	fmt.Printf("%p \n", &slice) //0xc0000044a0
	newSlice := append(slice, 4)
	fmt.Printf("%p \n", &newSlice) // 0xc0000044e0
	fmt.Printf("%p \n", &newSlice[0]) //0xc00000e380
	fmt.Printf("%p \n", &newSlice[1]) //0xc00000e388
	fmt.Printf("%p \n", &newSlice[2]) //0xc00000e390
	fmt.Printf("%p \n", &newSlice[3]) //0xc00000e398
	fmt.Println(newSlice)
	slice = append(slice, 5)
	fmt.Println(slice)
	fmt.Printf("%p \n", &slice) // 0xc0000044a0
	fmt.Printf("%p \n", &slice[0]) //0xc00000e380
	fmt.Printf("%p \n", &slice[1]) //0xc00000e388
	fmt.Printf("%p \n", &slice[2]) //0xc00000e390
	fmt.Printf("%p \n", &slice[3]) //0xc00000e398
	fmt.Println(newSlice)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值