int在结构中的奇怪行为

Gon*_*lez 4 atomic go uint32

假设我们有这种结构(有史以来最简单的结构之一):

type some struct{
    I uint32
}
Run Code Online (Sandbox Code Playgroud)

我们希望有一个这种类型的变量,并在for循环中原子递增(可能在另一个goroutine,但现在故事是不同的).我做以下事情:

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

我们得到了我们期望的东西,到目前为止一直很好,但是如果我们为这种类型声明一个函数如下:

func (sm some) Add1(){
    atomic.AddUint32(&sm.I,1)
}
Run Code Online (Sandbox Code Playgroud)

并在上面的示例(行[1])中调用此函数,该值不会递增,我们只是得到零.问题很明显 - 为什么?

这必须是基本的东西,但由于我是新手,我没有意识到.

pet*_*rSO 7

Go编程语言规范

呼叫

在函数调用中,函数值和参数按通常顺序计算.在评估它们之后,调用的参数通过值传递给函数,并且被调用的函数开始执行.当函数返回时,函数的返回参数通过值传递回调用函数.

接收器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)
}
Run Code Online (Sandbox Code Playgroud)

输出:

{1}
Run Code Online (Sandbox Code Playgroud)

Go常见问题(FAQ)

函数参数何时通过值传递?

与C系列中的所有语言一样,Go中的所有内容都按值传递.也就是说,函数总是获取正在传递的东西的副本,就好像有一个赋值语句将值赋给参数.例如,将int值传递给函数会生成int的副本,并且传递指针值会生成指针的副本,但不会生成它指向的数据.

我应该在值或指针上定义方法吗?

func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct)  valueMethod()   { } // method on value
Run Code Online (Sandbox Code Playgroud)

对于不习惯指针的程序员来说,这两个例子之间的区别可能令人困惑,但实际情况非常简单.在类型上定义方法时,接收器(上例中的s)的行为就像它是方法的参数一样.然后,将函数参数定义为值还是指针,将接收器定义为值还是指针都是同一个问题.有几个注意事项.

首先,最重要的是,该方法是否需要修改接收器?如果是,接收器必须是指针.(切片和贴图充当引用,因此它们的故事更加微妙,但是例如在接收方必须仍然是指针的方法中更改切片的长度.)在上面的示例中,如果pointerMethod修改了字段s,调用者将看到这些更改,但是使用调用者参数的副本调用valueMethod(这是传递值的定义),因此对调用者所做的更改将对调用者不可见.

顺便说一句,指针接收器与Java中的情况相同,尽管在Java中,指针隐藏在封面下; Go的价值接收器是不寻常的.

其次是效率的考虑.如果接收器很大,例如一个大的结构,使用指针接收器会便宜得多.

接下来是一致性.如果该类型的某些方法必须具有指针接收器,则其余方法也应如此,因此无论使用何种类型,方法集都是一致的.有关详细信息,请参阅方法集部分.

对于诸如基本类型,切片和小结构之类的类型,值接收器非常便宜,因此除非方法的语义需要指针,否则值接收器是高效且清晰的.