天天看點

go程式開發者們很容易犯的for循環陷阱

看如下的go程式demo

循環讀取一個slice,把讀取的值放入另一個slice

package main

import "fmt"

type Foo struct {
    bar string
}

func main() {
    list := []Foo{
        {"A"},
        {"B"},
        {"C"},
    }
    list2 := make([]*Foo, len(list))
    for i, value := range list {
        //fmt.Printf("%p\n",&value)
        list2[i] = &value
    }
    fmt.Println(list[0], list[1], list[2])
    fmt.Println(list2[0], list2[1], list2[2])
}      

理想中的預期運作結果應該是

{A} {B} {C}
&{A} &{B} &{C}      

但是當你實際運作後,結果不是這樣了

{A} {B} {C}
&{C} &{C} &{C}      

為什麼是這樣的結果呢?

首先我們應該知道, ​

for

​ 循環中,初始變量會被重用,例如: ​

i

​ , ​

value

​ ,在 ​

for

​ 之前被配置設定記憶體位址,之後循環隻是一直對這個記憶體位址重新指派,我們可以把代碼注釋去掉看看執行結果

0xc0001041e0
0xc0001041e0
0xc0001041e0
{A} {B} {C}
&{C} &{C} &{C}      

從執行結果中,可以看到記憶體位址一樣,而在for循環最後元素時,

value

的值是

C

,而

&value

是一個指向

value

的記憶體位址,如果想得到預期的結果該怎麼做

package main

import "fmt"

type Foo struct {
    bar string
}

func main() {
    list := []Foo{
        {"A"},
        {"B"},
        {"C"},
    }
    list2 := make([]*Foo, len(list))
    for i, value := range list {
        //每次循環都重新聲明一個變量
        temp:=value
        fmt.Printf("%p\n",&temp)
        list2[i] = &temp
    }
    fmt.Println(list[0], list[1], list[2])
    fmt.Println(list2[0], list2[1], list2[2])
}
      

運作結果

0xc0001041e0
0xc0001041f0
0xc000104200
{A} {B} {C}
&{A} &{B} &{C}      

希望開發者寫代碼過程中,注意這個坑,不然很容易犯這個錯誤