go语言 for循环中进行append操作的坑 |
您所在的位置:网站首页 › GO语言append切片删除元素的坑 › go语言 for循环中进行append操作的坑 |
问题描述
在写代码时遇到了一个bug,最终定位后发现是在for range中对slice进行append地址操作,最终的结果slice中的值全部一样且为for range中的最后一个,将问题简化如下。 func main() { var nums = []int{1, 2, 3, 4, 5} var res []*int for _, num := range nums { res = append(res, &num) fmt.Println("num:", num) } for _, r := range res { fmt.Println("res:", *r) } }运行结果如下: num: 1 num: 2 num: 3 num: 4 num: 5 res: 5 res: 5 res: 5 res: 5 res: 5每次循转中num的值是正常的,但是由append构造的res中,全是nums的最后一个值。 问题出现原因 最终总结出原因是在for range语句中,创建了变量num且只被创建了一次。即num有自己的空间内存且地址在for循环过程中不变,循环过程中每次将nums中对应的值和num进行值传递。 如下代码将num的地址和res中实际的内容打印出来: func main() { var nums = []int{1, 2, 3, 4, 5} var res []*int for _, num := range nums { res = append(res, &num) fmt.Println("num:", num, "&num:", &num) } fmt.Println("res:", res) }运行结果如下: num: 1 &num: 0xc0000b2008 num: 2 &num: 0xc0000b2008 num: 3 &num: 0xc0000b2008 num: 4 &num: 0xc0000b2008 num: 5 &num: 0xc0000b2008 res: [0xc0000b2008 0xc0000b2008 0xc0000b2008 0xc0000b2008 0xc0000b2008]可以看出变量num的值随循环在变但地址不变,印证了上述说法。即res中实际append了5次变量num的地址,只要对num的值进行更改,那么res中所有的值都会更改,所以出现了res中的值全部等于nums中最后一个值的情况。 解决方法 思路很明显,目的就是让append进res中的变量是不同的变量即可,目前能想到有两个方法: 在for循环中每次再定义一个新的变量num_temp,将num的值传给num_temp,之后append该变量即可。代码如下:func main() { var nums = []int{1, 2, 3, 4, 5} var res []*int for _, num := range nums { numTemp := num // 创建一个新的临时变量 res = append(res, &numTemp) fmt.Println("num:", num, "&num:", &num) fmt.Println("numTemp:", numTemp, "&numTemp:", &numTemp) } fmt.Println("res:", res) for _, r := range res { fmt.Println("res:", *r) } } 运行结果: num: 1 &num: 0xc0000b2008 numTemp: 1 &numTemp: 0xc0000b2010 num: 2 &num: 0xc0000b2008 numTemp: 2 &numTemp: 0xc0000b2030 num: 3 &num: 0xc0000b2008 numTemp: 3 &numTemp: 0xc0000b2038 num: 4 &num: 0xc0000b2008 numTemp: 4 &numTemp: 0xc0000b2040 num: 5 &num: 0xc0000b2008 numTemp: 5 &numTemp: 0xc0000b2048 res: [0xc0000b2010 0xc0000b2030 0xc0000b2038 0xc0000b2040 0xc0000b2048] res: 1 res: 2 res: 3 res: 4 res: 5不使用for range的形式,直接用索引来对nums取值。代码如下: func main() { var nums = []int{1, 2, 3, 4, 5} var res []*int for i := 0; i < len(nums); i++ { res = append(res, &nums[i]) fmt.Println("nums:", nums[i], "&nums:", &nums[i]) } fmt.Println("res:", res) for _, r := range res { fmt.Println("res:", *r) } } 运行结果: nums: 1 &nums: 0xc00001c180 nums: 2 &nums: 0xc00001c188 nums: 3 &nums: 0xc00001c190 nums: 4 &nums: 0xc00001c198 nums: 5 &nums: 0xc00001c1a0 res: [0xc00001c180 0xc00001c188 0xc00001c190 0xc00001c198 0xc00001c1a0] res: 1 res: 2 res: 3 res: 4 res: 5需要注意第二种方法节省了空间但是本质上nums和res两个slice使用了一个底层数组,修改任意一个slice的数据会导致两个slice内容都会更改。
|
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |