golang爬坑筆記之自問自答系列(1)——range函數的值拷貝遍歷

 代碼如下:

package main

import (
	"fmt"
	"time"
)

type Int struct {
	int int
}
func multi(i *Int) {
	i.int = i.int*100
}
func main() {
	a := []Int {
		{1},
		{2},
		{3},
		{4},
	}
	for _, v := range a{
		multi(&v)
		go func() {
			ticker := time.NewTicker(time.Second)
			<- ticker.C
			fmt.Println(v)
		}()
	}
	time.Sleep(time.Second*5)

	fmt.Println(a)
}

理想輸出結果應該爲:

{100}
{200}
{300}
{400}
{{100} {200} {300} {400}}

實際輸出爲:

{400}
{400}
{400}
{400}
{{1} {2} {3} {4}}

問題一:爲何v值一樣?

答:在每個循環中開啓的新goroutine時有停頓一秒,循環已經結束,所有的goroutine裏面的v值是指向同一個地址的值,即最後一次循環的v值。

問題二:爲何數組值沒有改變?

在回答二問題的時候,我還需要列上一組代碼與結果,如下:

package main

import (
	"fmt"
)

type Int struct {
	int int
}
func multi(i *Int) {
	i.int = i.int*100
}


func main() {
	a := []Int {
		{1},
		{2},
		{3},
		{4},
	}
	var b = make([]*Int, len(a))
	for i, v := range a{
		multi(&v)
		b[i] = &v
	}
	fmt.Println(a)
	fmt.Println(b[0],b[1],b[2],b[3])
}

結果:

[{1} {2} {3} {4}]
&{400} &{400} &{400} &{400}

答:在Golang的range方法中,始終使用值拷貝的方式代替被遍歷的元素本身,簡單來說,就是for…range中那個value,是一個值拷貝,而不是元素本身。這樣一來,當我們期望用&獲取元素的指針地址時,實際上只是取到了value這個臨時變量的指針地址,而非list中真正被遍歷到的某個元素的指針地址。因此上訴結果也就得到了完美的解釋。

可再看下例補充:

package main

import "fmt"

func main() {
	a := []int {1,2,3,4}
	for i, v := range a{
		fmt.Println(&v,&a[i])
	}
}

結果:

0xc000016078 0xc000018100
0xc000016078 0xc000018108
0xc000016078 0xc000018110
0xc000016078 0xc000018118

所以,range的坑搞懂了。嘿嘿

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章