Go語言中for range的"坑"

前言

Go 中的for range組合能夠和方便的實現對一個數組或切片進行遍歷,可是在某些狀況下使用for range時極可能就會被"坑",下面用一段代碼來模擬下:前端

func main() {
    arr1 := []int{1, 2, 3}
    arr2 := make([]*int, len(arr1))

    for i, v := range arr1 {
        arr2[i] = &v
    }

    for _, v := range arr2 {
        fmt.Println(*v)
    }
}

代碼解析:git

  1. 建立一個int slice,變量名爲arr1並初始化 1,2,3 做爲切片的值。
  2. 建立一個*int slice,變量名爲arr2
  3. 經過for range遍歷arr1,而後獲取每個元素的指針,賦值到對應arr2中。
  4. 逐行打印arr2中每一個元素的值。

從代碼上看,打印出來的結果應該是github

1
2
3

然而真正的結果是golang

3
3
3

緣由

由於for range在遍歷值類型時,其中的v變量是一個的拷貝,當使用&獲取指針時,其實是獲取到v這個臨時變量的指針,而v變量在for range中只會建立一次,以後循環中會被一直重複使用,因此在arr2賦值的時候其實都是v變量的指針,而&v最終會指向arr1最後一個元素的值拷貝。docker

來看看下面這個代碼,用for i來模擬for range,這樣更易於理解:數組

func main() {
    arr1 := []int{1, 2, 3}
    arr2 := make([]*int, len(arr1))

    var v int
    for i:=0;i<len(arr1);i++ {
        v = arr1[i]
        arr2[i] = &v
    }

    for _, v := range arr2 {
        fmt.Println(*v)
    }
}

解決方案

  1. 傳遞原始指針
func main() {
    arr1 := []int{1, 2, 3}
    arr2 := make([]*int, len(arr1))

    for i := range arr1 {
        arr2[i] = &arr1[i]
    }

    for _, v := range arr2 {
        fmt.Println(*v)
    }
}
  1. 使用臨時變量
func main() {
    arr1 := []int{1, 2, 3}
    arr2 := make([]*int, len(arr1))

    for i, v := range arr1 {
        t := v
        arr2[i] = &t
    }

    for _, v := range arr2 {
        fmt.Println(*v)
    }
}
  1. 使用閉包
func main() {
    arr1 := []int{1, 2, 3}
    arr2 := make([]*int, len(arr1))

    for i, v := range arr1 {
        func(v int){
             arr2[i] = &v
        }(v)
    }

    for _, v := range arr2 {
        fmt.Println(*v)
    }
}

官方提示

因爲這一問題過於廣泛,Golang甚至將其寫入了文檔的『常見錯誤』部分:文檔閉包

我是 MonkeyWie,歡迎掃碼👇👇關注!不按期在公衆號中分享 JAVAGolang前端dockerk8s等乾貨知識。

wechat

相關文章
相關標籤/搜索