go語言方法Value Receiver, Pointer Receiver各種不同情況的實驗筆記

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。注意: 此文章只是我的個人筆記,如有謬誤,錯誤, 請一定指出!
for range 問題
http://stackoverflow.com/questions/30577212/go-for-range-slice-and-goroutine-method-invocation-the-logic-behind
package mainimport (      "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 mainimport (      "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 = 1fmt.Printf("main, p: %p, v: %d\n", &a, a)a.ValueReceiver()p := &ap.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 := &apf.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結論:通過分析以上地址, 對於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 = 1fmt.Printf("main, p: %p, v: %d\n", &a, a)a.PointerReceiver()p := &ap.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 := &ap.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
結論: 通過以上地址的分析, 對於pointer receiver method, 在調用時, 不會產生副本,而是原對象本身的地址;也就是說沒有複製; 在(for _, a := range as) 中的a會被複用。---------------------------------------------------------------------------------------------------參考資料:
https://gobyexample.com/http://colobu.com/2015/09/07/gotchas-and-common-mistakes-in-go-golang/http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/http://waterdudu.github.io/post/go-tips/https://www.yushuangqi.com/blog/2015/7_things-you-may-not-pay-attation-to-in-go.htmlhttp://www.qiukeke.com/2015/05/28/gotchas-and-common-mistakes-in-go-golang.htmlhttp://tonybai.com/2015/09/17/7-things-you-may-not-pay-attation-to-in-go/

注意: 此文章只是我個人筆記, 如有錯漏,請一定指正, 共同學習, 我的郵箱: htyu_0203_39@sina.com


相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.