go语言坑之for range
2017-09-14 23:23
926 查看
编程中总会遇到各种莫名奇妙的坑,正确掌握语言使用姿势,能够有效避免踩坑闪着腰.
go只提供了一种循环方式,即for循环,在使用时可以像c那样使用,也可以通过for range方式遍历容器类型如数组、切片和映射。但是在使用for range时,如果使用不当,就会出现一些问题,导致程序运行行为不如预期。
程序设计的原意是遍历字符串切片并将其打印出来,可是输出结果却只输出了切片的最后一个元素.
原因是 : 闭包里引用了不作为参数传递进去的值,都是引用传递…也就是说,
如果要正确打印,在定义闭包的时候要定义一个参数,将v作为参数传递进去.修改后代码如下:
此外,输出结果不一定是 i am Sergey,因为goroutine执行顺序有go runtime调度器决定
原因解释:但是由输出可以知道,映射的值都相同且都是3。其实可以猜测映射的值都是同一个地址,遍历到切片的最后一个元素3时,将3写入了该地址,所以导致映射所有值都相同。其实真实原因也是如此,因为for range创建了每个元素的副本,而不是直接返回每个元素的引用,如果使用该值变量的地址作为指向每个元素的指针,就会导致错误,在迭代时,返回的变量是一个迭代过程中根据切片依次赋值的新变量,所以值的地址总是相同的,导致结果不如预期。
做一下小小的修改就能得到我们的预期结果:
go只提供了一种循环方式,即for循环,在使用时可以像c那样使用,也可以通过for range方式遍历容器类型如数组、切片和映射。但是在使用for range时,如果使用不当,就会出现一些问题,导致程序运行行为不如预期。
情形 . 一
> for range + 闭包代码如下: (闭包相关可以看我前面一篇博客)
package main import ( "fmt" "time" ) func main() { str := []string{"I","am","Sergey"} for _,v := range str{ go func() { fmt.Println(v) }() } time.Sleep(3 * tim 4000 e.Second) } ---------- 输出结果: Sergey Sergey Sergey
程序设计的原意是遍历字符串切片并将其打印出来,可是输出结果却只输出了切片的最后一个元素.
原因是 : 闭包里引用了不作为参数传递进去的值,都是引用传递…也就是说,
println(v)其实是引用了v的地址然后解引用,将值打印出来..等到这个goroutine执行
println(v)的时候,v所指向的值已经是”Sergey”
如果要正确打印,在定义闭包的时候要定义一个参数,将v作为参数传递进去.修改后代码如下:
package main import ( "fmt" "time" ) func main() { str := []string{"I","am","Sergey"} for _,v := range str{ go func(v string) { fmt.Println(v) }(v) } time.Sleep(3 * time.Second) }
此外,输出结果不一定是 i am Sergey,因为goroutine执行顺序有go runtime调度器决定
情形 . 二
> 操作map,原理和情形一类似代码如下:
package main import "fmt" func main() { slice := []int{0, 1, 2, 3} myMap := make(map[int]*int) for index , value := range slice { myMap[index] = &value } prtMap(myMap) } func prtMap(myMap map[int]*int) { for key, value := range myMap { fmt.Printf("map[%v]=%v\n", key, *value) } } ---------- 输出结果: map[0]=3 map[1]=3 map[2]=3 map[3]=3
原因解释:但是由输出可以知道,映射的值都相同且都是3。其实可以猜测映射的值都是同一个地址,遍历到切片的最后一个元素3时,将3写入了该地址,所以导致映射所有值都相同。其实真实原因也是如此,因为for range创建了每个元素的副本,而不是直接返回每个元素的引用,如果使用该值变量的地址作为指向每个元素的指针,就会导致错误,在迭代时,返回的变量是一个迭代过程中根据切片依次赋值的新变量,所以值的地址总是相同的,导致结果不如预期。
做一下小小的修改就能得到我们的预期结果:
package main import "fmt" func main() { slice := []int{0, 1, 2, 3} myMap := make(map[int]*int) for index , value := range slice { v := value myMap[index] = &v } prtMap(myMap) } func prtMap(myMap map[int]*int) { for key, value := range myMap { fmt.Printf("map[%v]=%v\n", key, *value) } }
相关文章推荐
- 学习Go语言的某for range教程引出的问题
- 学习-go语言坑之for range
- Go语言技巧:使用for range time.Tick()固定间隔时间执行
- go语言坑之for range
- 【go语言】wait,wait for me
- 32.笔记go语言——range关键字
- Go语言中的range用法实例分析
- go语言中range用法
- go学习(十)——Go 语言范围(Range)
- Go 语言范围(Range)
- GO语言range的用法 (2013-08-09 14:08:26)
- Go语言的for和switch
- Go 语言范围(Range)
- Go语言范围(Range)
- Go中使用for..range语句时需要注意的地方
- ODBC database driver for Go:Go语言通过ODBC 访问SQL server
- go语言示例:for语句
- Go语言 for
- go语言循环语句 for
- Go语言学习笔记十二: 范围(Range)