go语言坑之for range

转载 2018年04月16日 11:28:16

go只提供了一种循环方式,即for循环,在使用时可以像c那样使用,也可以通过for range方式遍历容器类型如数组、切片和映射。但是在使用for range时,如果使用不当,就会出现一些问题,导致程序运行行为不如预期。比如,下面的示例程序将遍历一个切片,并将切片的值当成映射的键和值存入,切片类型是一个int型,映射的类型是键为int型,值为*int,即值是一个地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
    }
    fmt.Println("=====new map=====")
    prtMap(myMap)
}
 
func prtMap(myMap map[int]*int) {
    for key, value := range myMap {
        fmt.Printf("map[%v]=%v\n", key, *value)
    }
}

  运行程序输出如下:

1
2
3
4
5
=====new map=====
map[3]=3
map[0]=3
map[1]=3
map[2]=3

  由输出可以知道,不是我们预期的输出,正确输出应该如下:

1
2
3
4
5
=====new map=====
map[0]=0
map[1]=1
map[2]=2
map[3]=3

  

但是由输出可以知道,映射的值都相同且都是3。其实可以猜测映射的值都是同一个地址,遍历到切片的最后一个元素3时,将3写入了该地址,所以导致映射所有值都相同。其实真实原因也是如此,因为for range创建了每个元素的副本,而不是直接返回每个元素的引用,如果使用该值变量的地址作为指向每个元素的指针,就会导致错误,在迭代时,产生的value相当于是一个临时变量,而且每次迭代是用的同一个临时变量,所以其内存地址总是相同的,如果是按上面的写法,每次迭代都给map赋了相同的指针,而做完遍历之后,指针所指向地址里面存放的值也就是最后一次迭代的值,也就是3,导致结果不如预期。

修正后程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
 
import "fmt"
 
func main() {
    slice := []int{0, 1, 2, 3}
    myMap := make(map[int]*int)
 
    for index, value := range slice {
        num := value
        myMap[index] = &num
    }
    fmt.Println("=====new map=====")
    prtMap(myMap)
}
 
func prtMap(myMap map[int]*int) {
    for key, value := range myMap {
        fmt.Printf("map[%v]=%v\n", key, *value)
    }
}

  运行程序输出如下:

1
2
3
4
5
=====new map=====
map[2]=2
map[3]=3
map[0]=0
map[1]=1

  

快速的执行是成功的关键!

Go语言的那些坑

Golang是我最喜欢的一门语言,它简洁、高效、易学习、开发效率高、还可以编译成机器码… 虽然它一出世,就饱受关注,而且现在在市面上逐渐流行开来,但是,它毕竟是一门新兴语言,还有很多让人不太习惯的地...
  • weiyuefei
  • weiyuefei
  • 2017年09月13日 16:24
  • 686

Go 语言范围(Range)

Go 语言范围(Range)Go 语言中 range 关键字用于for循环中迭代数组(array)、切片(slice)、链表(channel)或集合(map)的元素。在数组和切片中它返回元素的索引值,...
  • u011225629
  • u011225629
  • 2015年11月29日 16:47
  • 2734

go语言坑之for range

go只提供了一种循环方式,即for循环,在使用时可以像c那样使用,也可以通过for range方式遍历容器类型如数组、切片和映射。但是在使用for range时,如果使用不当,就会出现一些问题,导致程...
  • leo881205
  • leo881205
  • 2017年04月04日 11:30
  • 191

Go语言范围(Range)

package main import "fmt" func main () { /*这是我们使用range去求一个slice的和。*/ nums := []int {2,3,4} sum :...
  • chou_out_man
  • chou_out_man
  • 2017年07月08日 15:21
  • 91

golang range 遍历

问题描述:遍历一个数组,修改其中的值:type MyTest struct { X int }arr := make([]MyTest,0,20) for k,v := range arr{ ...
  • qq_17612199
  • qq_17612199
  • 2017年10月06日 17:24
  • 323

Go语言核心之美-必读

Go语言核心技术系列文章都是我原创、或从国外的书籍和博客中收集优秀的文章进行翻译的,因此转载时请注明转载出处,不胜感激。 欢迎大家加入Go语言核心技术QQ群894864,里面热心大神很多哦!...
  • abv123456789
  • abv123456789
  • 2016年03月14日 13:34
  • 3915

Go踩坑之 time.Parse: month out of range

在用Go语言对时间字符串进行parse的时候,经过 试验,“2016-12-25 00:00:00”这种格式是可以的, “2016-12-25“这种格式在试验的时候可以,然而在某些情况却会报错,比...
  • jhgdike
  • jhgdike
  • 2018年02月02日 20:22
  • 207

GO 使用channel进行同步 (缓冲channel)

上一篇文章提到了普通的channel用来进行线程的同步。Go语言里面还有一种缓冲式的channel。 ch := make(chan int, 100) 需要执行类型和缓冲区的大小。 packa...
  • genispan
  • genispan
  • 2015年02月03日 21:55
  • 3973

go语言常见陷阱

go语言常见陷阱(英文原文)[https://deadbeef.me/2018/01/go-gotchas]Range在golang中我们经常用range来遍历slice或chan,如果要更改slic...
  • KingEasternSun
  • KingEasternSun
  • 2018年01月03日 15:09
  • 178

Go lang学习第四篇数组,切片,关联数组,Range遍历

一、数组 package main import "fmt" func main() { //这里我们创建一个数组`a`来存放刚好5个`int` //元素的类型和长度都是数组类型的一部分。...
  • aliaichidantong
  • aliaichidantong
  • 2018年01月11日 15:42
  • 121
收藏助手
不良信息举报
您举报文章:go语言坑之for range
举报原因:
原因补充:

(最多只允许输入30个字)