1、指针数据坑
range 到底有什么坑呢,我们先来运行一个例子吧。
package main import ( "fmt" ) type user struct { name string age uint64 } func main() { u := []user{ {"asong",23}, {"song",19}, {"asong2020",18}, } n := make([]*user,0,len(u)) for _,v := range u{ n = append(n, &v) } fmt.Println(n) for _,v := range n{ fmt.Println(v) } }
这个例子的目的是,通过 **u **这个 slice 构造成新的 slice。我们预期应该是显示 u slice 的内容,但是运行结果如下:
[0xc0000a6040 0xc0000a6040 0xc0000a6040] &{asong2020 18} &{asong2020 18} &{asong2020 18}
这里我们看到 n 这个 slice 打印出来的三个同样的数据,并且他们的内存地址相同。这是什么原因呢?先别着急,再来看这一段代码,我给他改正确他,对比之后我们再来分析,你们才会恍然大悟。
package main import ( "fmt" ) type user struct { name string age uint64 } func main() { u := []user{ {"asong",23}, {"song",19}, {"asong2020",18}, } n := make([]*user,0,len(u)) for _,v := range u{ o := v n = append(n, &o) } fmt.Println(n) for _,v := range n{ fmt.Println(v) } }
细心的你们看到,我改动了哪一部分代码了嘛?对,没错,我就加了一句话,他就成功了,我在 for range 里面引入了一个中间变量,每次迭代都重新声明一个变量 o,赋值后再将 v 的地址添加 n 切片中,这样成功解决了刚才的问题。
现在来解释一下原因:在 for range 中,变量 v 是用来保存迭代切片所得的值,因为 v 只被声明了一次,每次迭代的值都是赋值给 v,该变量的内存地址始终未变,这样讲他的地址追加到新的切片中,该切片保存的都是同一个地址,这肯定无法达到预期效果的。这里还需要注意一点,变量 v 的地址也并不是指向原来切片 u 的,因我在使用 range 迭代的时候,变量 v 的数据是切片的拷贝数据,所以直接 copy 了结构体数据。
上面的问题还有一种解决方法,直接引用数据的内存,这个方法比较好,不需要开辟新的内存空间,看以下代码:
......略 for k,_ := range u{ n = append(n, &u[k]) } ......略
2、迭代修改变量的问题
还是刚才的例子,我们做一点改动,现在我们要对切片中保存的每个用户的年龄进行修改,因为我们都是永远 18 岁,嘎嘎嘎~~~。
package main import ( "fmt" ) type user struct { name string age uint64 } func main() { u := []user{ {"asong",23}, {"song",19}, {"asong2020",18}, } for _,v := range u{ if v.age != 18{ v.age = 20 } } fmt.Println(u) }
来看一下运行结果:
[{asong 23} {song 19} {asong2020 18}]
哎呀,怎么回事。怎么没有更改呢。其实道理都是一样,还记得,我在上文说的一个知识点嘛。对,就是这个,想起来了吧。v 变量是拷贝切片中的数据,修改拷贝数据怎么会对原切片有影响呢,还是这个问题,copy 这个知识点很重要,一不注意,就会出现问题。知道问题了,我们现在来把这个问题解决吧。
package main import ( "fmt" ) type user struct { name string age uint64 } func main() { u := []user{ {"asong",23}, {"song",19}, {"asong2020",18}, } for k,v := range u{ if v.age != 18{ u[k].age = 18 } } fmt.Println(u) }
可以看到,我们直接对切片的值进行修改,这样就修改成功了。所以这里还是要注意一下的,防止以后出现 bug。
3、是否会造成死循环
来看一段代码:
func main() { v := []int{1, 2, 3} for i := range v { v = append(v, i) } }
这一段代码会造成死循环吗?答案:当然不会,前面都说了 range 会对切片做拷贝,新增的数据并不在拷贝内容中,并不会发生死循环。这种题一般会在面试中问,可以留意下的。
原文地址:https://www.blsa.cn/articles/173
以上解释看不懂?可以继续看下本篇博文的解释:golang for range循环_go语言的中的for range循环之大坑
声明:禁止任何非法用途使用,凡因违规使用而引起的任何法律纠纷,本站概不负责。
精彩评论