1. 程式人生 > >Go:坑之for range

Go:坑之for range

go只提供了一種迴圈方式,即for迴圈,在使用時可以像c那樣使用,也可以通過for range方式遍歷容器型別如陣列、切片和對映。但是在使用for range時,如果使用不當,就會出現一些問題,導致程式執行行為不如預期。比如,下面的示例程式將遍歷一個切片,並將切片的值當成對映的鍵和值存入,切片型別是一個int型,對映的型別是鍵為int型,值為*int,即值是一個地址。

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)
    }
}

  執行程式輸出如下:

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

  由輸出可以知道,不是我們預期的輸出,正確輸出應該如下:

=====new map=====
map[0]=0
map[1]=1
map[2]=2
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 {
        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)
    }
}

  執行程式輸出如下:

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