让我们说我们有这种结构(有史以来最简单的结构之一):
type some struct{
I uint32
}
我们希望有一个这种类型的变量,并在for循环中原子递增(可能在另一个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 :(得分:7)
The Go Programming Language Specification
在函数调用中,将计算函数值和参数 通常的顺序。在评估它们之后,调用的参数 通过值传递给函数,并且被调用的函数开始 执行。函数的返回参数按值传递 当函数返回时返回调用函数。
接收方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系列中的所有语言一样,Go中的所有内容都通过 值。也就是说,一个函数总是得到一个东西的副本 传递,好像有一个赋值的赋值语句 到参数。例如,将int值传递给函数 制作int的副本,并传递指针值制作副本 指针,但不指向它指向的数据。
Should I define methods on values or pointers?
func (s *MyStruct) pointerMethod() { } // method on pointer func (s MyStruct) valueMethod() { } // method on value
对于不习惯指针的程序员来说,区别 这两个例子可能令人困惑,但事实上情况确实如此 非常简单。在类型上定义方法时,接收器(在 上面的例子)就像它是一个参数一样 方法。是将接收器定义为值还是指针 同样的问题,那么,函数参数是否应该是a 值或指针。有几点需要考虑。
首先,最重要的是,该方法是否需要修改 接收器?如果是,接收器必须是指针。 (切片和地图 作为参考,所以他们的故事更微妙,但是 在接收器必须的方法中更改切片长度的实例 仍然是一个指针。)在上面的例子中,如果pointerMethod修改 在s的字段中,调用者将看到这些更改,但valueMethod是 用调用者的参数的副本调用(这是定义的 传递一个值,所以改变它使得调用者看不见。
顺便说一句,指针接收器与Java中的情况相同, 虽然在Java中,指针隐藏在封面下;这是Go的 价值接收器是不寻常的。
其次是效率的考虑。如果接收器很大,a 例如,大结构,使用指针要便宜得多 接收机。
接下来是一致性。如果该类型的某些方法必须具有 指针接收器,其余的也应该是,所以方法集是 无论使用何种类型,都是一致的。请参阅有关的部分 方法设置细节。
对于基本类型,切片和小结构等类型,值 接收器非常便宜,所以除非方法的语义要求 一个指针,一个值接收器是高效和清晰的。
答案 1 :(得分: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)
}