2016-03-05 279 views
4

比方說,我們有這樣的一個結構(最簡單的一個曾經):奇怪的行爲

type some struct{ 
    I uint32 
} 

我們希望有這種類型的變量,可以原子方式增加在循環(可能在另一個goroutine中,但現在的故事是不同的)。我做到以下幾點:

q := some{0} 
for i := 0; i < 10; i++ { 
     atomic.AddUint32(&q.I,1) // increment [1] 
     fmt.Println(q.I) 
} 

我們得到了我們所期待,到目前爲止好,但如果我們定義一個函數,該函數類型如下:

func (sm some) Add1(){ 
    atomic.AddUint32(&sm.I,1) 
} 

,並調用這個函數在上面的示例(行[1])中,值不會遞增,我們只會得到零。這個問題很明顯 - 爲什麼?

這必須是基本的東西,但因爲我是新來的,我沒有意識到它。

+0

對Go語言指針的Tha方法有點混亂。在Go中,您可以定義一個方法接收器來指定哪個結構附加某個函數,以便將其作爲方法調用。 –

回答

7

The Go Programming Language Specification

Calls

在一個函數調用,該函數值和參數在 通常的順序進行評估。評估完成後,呼叫 的參數將按值傳遞給函數,被調用的函數將開始執行 。當函數返回時,函數的返回參數通過值 傳回給調用函數。

將接收方sm some按值傳遞給方法,並且從方法返回時將丟棄副本。使用指針接收器。

例如,

package main 

import (
    "fmt" 
    "sync/atomic" 
) 

type some struct { 
    I uint32 
} 

func (sm *some) Add1() { 
    atomic.AddUint32(&sm.I, 1) 
} 

func main() { 
    var s some 
    s.Add1() 
    fmt.Println(s) 
} 

輸出:

{1} 

Go Frequently Asked Questions (FAQ)

When are function parameters passed by value?

如在C系列中的所有語言,一切都在走的是通過b y值爲 。也就是說,函數總是獲得 傳遞的副本,就像有一個賦值語句將值 分配給該參數一樣。例如,將int值傳遞給函數 將生成int的副本,並且傳遞一個指針值會使指針的拷貝 ,但不會指向它所指向的數據。

Should I define methods on values or pointers?

func (s *MyStruct) pointerMethod() { } // method on pointer 
func (s MyStruct) valueMethod() { } // method on value 

對於程序員來說不習慣指針, 之間的區別這兩個例子可能會造成混淆,但情況其實 很簡單。在類型上定義方法時,接收者(位於上述示例中的 中)的行爲完全如同它是 方法的參數。是否將接收器定義爲值或作爲指針是 相同的問題,那麼,作爲函數參數應該是 值還是指針。有幾個考慮因素。

首先,最重要的是,該方法是否需要修改接收器?如果是這樣,接收器必須是一個指針。 (切片和地圖 充當引用,所以他們的故事更微妙一點,但對於 實例來說,在一個方法中改變切片的長度時,接收器必須仍然是一個指針 )。在上面的例子中,如果pointerMethod修改 s的字段,調用者將看到這些更改,但valueMethod是 用調用者參數的副本調用(這是 傳遞值的定義),因此調用者看不到它的更改。

順便提一句,指針接收器與Java中的情況相同, 雖然在Java中指針隱藏在封面之下;這是Go的 價值接收器是不尋常的。

二是對效率的考慮。如果接收器很大,例如一個大的結構,使用指針 接收器將會便宜得多。

接下來是一致性。如果某些類型的方法必須具有指針接收器,則其餘部分也應該如此,因此無論使用何種類型,該方法集均爲 。有關詳細信息,請參閱 方法集一節。

對於基本類型,切片和小型結構等類型,接收器的價格非常便宜,因此除非方法的語義需要 指針,否則值接收器是高效且清晰的。

2

你的函數需要接收一個指針,以增加值,這樣你不會傳遞結構的副本,並且在下一次迭代中我可以遞增。

package main 

import (
"sync/atomic" 
"fmt" 
) 

type some struct{ 
    I uint32 
} 

func main() { 
q := &some{0} 
for i := 0; i < 10; i++ { 
     q.Add1() 
     fmt.Println(q.I) 
} 
} 

func (sm *some) Add1(){ 
    atomic.AddUint32(&sm.I,1) 
}