注意: 此文章只是我的个人笔记,如有谬误,错误, 请一定指出!
for range 问题
for range 问题
package main
import (
"fmt"
"time"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Printf("print: p: %p, v: %s\n", p, p.name)
}
func main() {
data := []field{ {"one"},{"two"},{"three"} }
for _,v := range data { // 注意:for语句中的迭代变量(如: v)在每次迭代时被重新使用, 一直复用
go v.print() // 注意: 此处可理解为: go (&v).print(), 也就是用v的指针去调用, 而且v会在每次迭代时复用,所以每一个调用的receiver都是共同指向v的指针, 而且v在最后一次迭代后, 被 赋值为:"three", 所以 才有了打印出3个"three"的结果.
}
time.Sleep(3 * time.Second)
//goroutines print: three, three, three
}
package main
import (
"fmt"
"time"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func main() {
data := []*field{ {"one"},{"two"},{"three"} }
for _,v := range data {
go v.print() //v本身就是指针, 指向one, two, three; 迭代时会改变指向, 直接调用没有复制,直接调用,故每次调用时v都是分别指向one, two , three的地址值 , 当然会打印出正确的结果 ; 与上例不同, 上例中,因为v的复用,每一次调用的pointer receiver都共同指向v, 但v的值在循环后最终赋值为:three,
所以出现错误结果。
}
time.Sleep(3 * time.Second)
}
//goroutines print: one, two, three
注意:四个要素解释以上两例子:
前提条件:方法定义时为Pointer receiver.
func (p *field) print() {
fmt.Printf("print: p: %p, v: %s\n", p, p.name)
}
(1) 满足前提条件下, Pointer Receiver不复制,所以当以值方式调用 时, 直接 &value取地址作为pointer receiver.
(2)满足前提条件下,若for _,v := range data中的v本身就是指针, 则直接调用.
(3)for range会在每次迭代中复用v
(4)go语言本身是值语义的, 也就是说传参,调用, 迭代都会复制, 只不过像:指针, 引用类型只是复制了其本身, 而非其指向的data, 这样复制代价很低很低;不过数组为值类型的,会整体复制哟。 后而参考资料链接中有详细的分析。
//stackoverflow中的解释:
这在Go中是个很常见的技巧。for语句中的迭代变量在每次迭代时被重新使用。这就意味着你在for循环中创建的闭包(即函数字面量)将会引用同一个变量(而在那些goroutine开始执行时就会得到那个变量的值)。
In the first loop,v is thevalue of afield item. Becausev
is addressable, it is automatically referenced as the pointer receiver for theprint() method. Sov.print() is using the address ofv
itself, and the contents of that address is overwritten each iteration of the loop.
When you change the declaration to use a*field,v is now a pointer to afield
value. When you callv.print() in this case, you are operating on the value thatv points to, which is stored indata, and the overwriting
of v has no effect.
----------------------------------------------------我的试验代码---------------------------------
package main
import "fmt"
import "time"
type A int
func (a A) ValueReceiver(){
fmt.Printf("ValueReceiver, p: %p, v: %d\n", &a, a)
}
func (a A) ValueReceiverIngo(){
fmt.Printf("ValueReceiverIngo, p: %p, v:%d\n", &a, a)
}
func (a A) ValueReceiverInDefer(){
fmt.Printf("ValueReceiverInDefer, p: %p, v:%d\n", &a, a)
}
func (a A) ValueRececiverInforRange(){
fmt.Printf("ValueRececiverInforRange, p: %p, v: %d\n", &a, a)
}
func main() {
var a A = 1
fmt.Printf("main, p: %p, v: %d\n", &a, a)
a.ValueReceiver()
p := &a
p.ValueReceiver()
//------------call in goroutine--------
go a.ValueReceiverIngo()
go p.ValueReceiverIngo()
time.Sleep(3* time.Second)
//---------call in defer----------
defer a.ValueReceiverInDefer()
defer p.ValueReceiverInDefer()
//---call in for range array, value receiver---
as := [5]A{1,2,3,4,5}
fmt.Printf("as[0]: %p, %d\n", &as[0], as[0])
for _, a := range as {
fmt.Printf("as in for: %p, %d\n", &as[0], as[0])
fmt.Printf("a in for: %p, v: %d\n", &a, a)
a.ValueRececiverInforRange()
pf := &a
pf.ValueRececiverInforRange()
}
}
//结果
main, p: 0x10434114, v: 1
ValueReceiver, p: 0x1043411c, v: 1
ValueReceiver, p: 0x10434134, v: 1
ValueReceiverIngo, p: 0x1043413c, v:1
ValueReceiverIngo, p: 0x10434144, v:1
as[0]: 0x10430240, 1
as in for: 0x10430240, 1
a in for: 0x10434150, v: 1
ValueRececiverInforRange, p: 0x1043415c, v: 1
ValueRececiverInforRange, p: 0x10434164, v: 1
as in for: 0x10430240, 1
a in for: 0x10434150, v: 2
ValueRececiverInforRange, p: 0x10434174, v: 2
ValueRececiverInforRange, p: 0x1043417c, v: 2
as in for: 0x10430240, 1
a in for: 0x10434150, v: 3
ValueRececiverInforRange, p: 0x1043418c, v: 3
ValueRececiverInforRange, p: 0x10434194, v: 3
as in for: 0x10430240, 1
a in for: 0x10434150, v: 4
ValueRececiverInforRange, p: 0x104341a4, v: 4
ValueRececiverInforRange, p: 0x104341ac, v: 4
as in for: 0x10430240, 1
a in for: 0x10434150, v: 5
ValueRececiverInforRange, p: 0x104341bc, v: 5
ValueRececiverInforRange, p: 0x104341c4, v: 5
ValueReceiverInDefer, p: 0x104341cc, v:1
ValueReceiverInDefer, p: 0x104341d4, v:1
ValueReceiver, p: 0x1043411c, v: 1
ValueReceiver, p: 0x10434134, v: 1
ValueReceiverIngo, p: 0x1043413c, v:1
ValueReceiverIngo, p: 0x10434144, v:1
as[0]: 0x10430240, 1
as in for: 0x10430240, 1
a in for: 0x10434150, v: 1
ValueRececiverInforRange, p: 0x1043415c, v: 1
ValueRececiverInforRange, p: 0x10434164, v: 1
as in for: 0x10430240, 1
a in for: 0x10434150, v: 2
ValueRececiverInforRange, p: 0x10434174, v: 2
ValueRececiverInforRange, p: 0x1043417c, v: 2
as in for: 0x10430240, 1
a in for: 0x10434150, v: 3
ValueRececiverInforRange, p: 0x1043418c, v: 3
ValueRececiverInforRange, p: 0x10434194, v: 3
as in for: 0x10430240, 1
a in for: 0x10434150, v: 4
ValueRececiverInforRange, p: 0x104341a4, v: 4
ValueRececiverInforRange, p: 0x104341ac, v: 4
as in for: 0x10430240, 1
a in for: 0x10434150, v: 5
ValueRececiverInforRange, p: 0x104341bc, v: 5
ValueRececiverInforRange, p: 0x104341c4, v: 5
ValueReceiverInDefer, p: 0x104341cc, v:1
ValueReceiverInDefer, p: 0x104341d4, v:1
结论:通过分析以上地址, 对于value receciver method, 各种调用方式下, 都是对于原值的复制, 也就是说,以副本为receiver调用, 即使是以指针方式调用,也是以*pointer 生成副本后再调用 ;同时也注意到for range中复用了a (for _, a := range as ).--------------------------------------------------------------------------------------
-------------------------------------Pointer Receiver Test--------------------------
package main
import "fmt"
import "time"
type A int
func(p *A)PointerReceiver(){
fmt.Printf("PointerReceiver, p: %p, v: %d\n", p, *p)
}
func(p *A)PointerReceiveringo(){
fmt.Printf("PointerReceiveringo, p: %p, v: %d\n", p, *p)
}
func(p *A)PointerReceiverinDefer(){
fmt.Printf("PointerReceiverinDefer, p: %p, v: %d\n", p, *p)
}
func(p *A)PointerReceiverinforRange(){
fmt.Printf("PointerReceiverinforRange, p: %p, v: %d\n", p, *p)
}
func main() {
var a A = 1
fmt.Printf("main, p: %p, v: %d\n", &a, a)
a.PointerReceiver()
p := &a
p.PointerReceiver()
//------------------
go a.PointerReceiveringo()
go p.PointerReceiveringo()
time.Sleep(3* time.Second)
//------------------
defer a.PointerReceiverinDefer()
defer p.PointerReceiverinDefer()
//------------------
as := [5]A{1,2,3,4,5}
fmt.Printf("as[0], p: %p, v: %d\n", &as[0], as[0])
for _, a := range as {
fmt.Printf("as in for: p: %p, v: %d\n", &as[0], as[0])
fmt.Printf("a in for: p: %p, v: %d\n", &a, a)
a.PointerReceiverinforRange()
p := &a
p.PointerReceiverinforRange()
}
}
结果:
main, p: 0x10434114, v: 1
PointerReceiver, p: 0x10434114, v: 1
PointerReceiver, p: 0x10434114, v: 1
PointerReceiveringo, p: 0x10434114, v: 1
PointerReceiveringo, p: 0x10434114, v: 1
as[0], p: 0x10430240, v: 1
as in for: p: 0x10430240, v: 1
a in for: p: 0x10434140, v: 1
PointerReceiverinforRange, p: 0x10434140, v: 1
PointerReceiverinforRange, p: 0x10434140, v: 1
as in for: p: 0x10430240, v: 1
a in for: p: 0x10434140, v: 2
PointerReceiverinforRange, p: 0x10434140, v: 2
PointerReceiverinforRange, p: 0x10434140, v: 2
as in for: p: 0x10430240, v: 1
a in for: p: 0x10434140, v: 3
PointerReceiverinforRange, p: 0x10434140, v: 3
PointerReceiverinforRange, p: 0x10434140, v: 3
as in for: p: 0x10430240, v: 1
a in for: p: 0x10434140, v: 4
PointerReceiverinforRange, p: 0x10434140, v: 4
PointerReceiverinforRange, p: 0x10434140, v: 4
as in for: p: 0x10430240, v: 1
a in for: p: 0x10434140, v: 5
PointerReceiverinforRange, p: 0x10434140, v: 5
PointerReceiverinforRange, p: 0x10434140, v: 5
PointerReceiverinDefer, p: 0x10434114, v: 1
PointerReceiverinDefer, p: 0x10434114, v: 1
PointerReceiver, p: 0x10434114, v: 1
PointerReceiver, p: 0x10434114, v: 1
PointerReceiveringo, p: 0x10434114, v: 1
PointerReceiveringo, p: 0x10434114, v: 1
as[0], p: 0x10430240, v: 1
as in for: p: 0x10430240, v: 1
a in for: p: 0x10434140, v: 1
PointerReceiverinforRange, p: 0x10434140, v: 1
PointerReceiverinforRange, p: 0x10434140, v: 1
as in for: p: 0x10430240, v: 1
a in for: p: 0x10434140, v: 2
PointerReceiverinforRange, p: 0x10434140, v: 2
PointerReceiverinforRange, p: 0x10434140, v: 2
as in for: p: 0x10430240, v: 1
a in for: p: 0x10434140, v: 3
PointerReceiverinforRange, p: 0x10434140, v: 3
PointerReceiverinforRange, p: 0x10434140, v: 3
as in for: p: 0x10430240, v: 1
a in for: p: 0x10434140, v: 4
PointerReceiverinforRange, p: 0x10434140, v: 4
PointerReceiverinforRange, p: 0x10434140, v: 4
as in for: p: 0x10430240, v: 1
a in for: p: 0x10434140, v: 5
PointerReceiverinforRange, p: 0x10434140, v: 5
PointerReceiverinforRange, p: 0x10434140, v: 5
PointerReceiverinDefer, p: 0x10434114, v: 1
PointerReceiverinDefer, p: 0x10434114, v: 1
结论: 通过以上地址的分析, 对于pointer receiver method, 在调用时, 不会生成副本,
而是原对象本身的地址;也就是说没有复制; 在(for _, a := range as) 中的a会被复用。
---------------------------------------------------------------------------------------------------
参考资料:
注意: 此文章只是我个人笔记, 如有错漏,请一定指正, 共同学习, 我的邮箱: htyu_0203_39@sina.com
有疑问加站长微信联系(非本文作者)