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的坑搞懂了。嘿嘿

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