结构内部 int 的奇怪行为
Strange behaviour of int inside a struct
假设我们有这种结构(有史以来最简单的结构之一):
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] 行)中调用此函数,值不会递增,我们只会得到零。问题很明显 - 为什么?
这必须是一些基本的东西,但由于我是新来的,所以我没有意识到。
The Go Programming Language Specification
In a function call, the function value and arguments are evaluated in
the usual order. After they are evaluated, the parameters of the call
are passed by value to the function and the called function begins
execution. The return parameters of the function are passed by value
back to the calling function when the function returns.
接收器 sm some
按值传递给方法,当您从方法中 return 时,副本将被丢弃。使用指针接收器。
例如,
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?
As in all languages in the C family, everything in Go is passed by
value. That is, a function always gets a copy of the thing being
passed, as if there were an assignment statement assigning the value
to the parameter. For instance, passing an int value to a function
makes a copy of the int, and passing a pointer value makes a copy of
the pointer, but not the data it points to.
Should I define methods on values or pointers?
func (s *MyStruct) pointerMethod() { } // method on pointer
func (s MyStruct) valueMethod() { } // method on value
For programmers unaccustomed to pointers, the distinction between
these two examples can be confusing, but the situation is actually
very simple. When defining a method on a type, the receiver (s in the
above examples) behaves exactly as if it were an argument to the
method. Whether to define the receiver as a value or as a pointer is
the same question, then, as whether a function argument should be a
value or a pointer. There are several considerations.
First, and most important, does the method need to modify the
receiver? If it does, the receiver must be a pointer. (Slices and maps
act as references, so their story is a little more subtle, but for
instance to change the length of a slice in a method the receiver must
still be a pointer.) In the examples above, if pointerMethod modifies
the fields of s, the caller will see those changes, but valueMethod is
called with a copy of the caller's argument (that's the definition of
passing a value), so changes it makes will be invisible to the caller.
By the way, pointer receivers are identical to the situation in Java,
although in Java the pointers are hidden under the covers; it's Go's
value receivers that are unusual.
Second is the consideration of efficiency. If the receiver is large, a
big struct for instance, it will be much cheaper to use a pointer
receiver.
Next is consistency. If some of the methods of the type must have
pointer receivers, the rest should too, so the method set is
consistent regardless of how the type is used. See the section on
method sets for details.
For types such as basic types, slices, and small structs, a value
receiver is very cheap so unless the semantics of the method requires
a pointer, a value receiver is efficient and clear.
你的函数需要接收一个指针来增加值,这样你就不会传递结构的副本,并且在下一次迭代中 I 可以增加。
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)
}
假设我们有这种结构(有史以来最简单的结构之一):
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] 行)中调用此函数,值不会递增,我们只会得到零。问题很明显 - 为什么?
这必须是一些基本的东西,但由于我是新来的,所以我没有意识到。
The Go Programming Language Specification
In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the calling function when the function returns.
接收器 sm some
按值传递给方法,当您从方法中 return 时,副本将被丢弃。使用指针接收器。
例如,
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?
As in all languages in the C family, everything in Go is passed by value. That is, a function always gets a copy of the thing being passed, as if there were an assignment statement assigning the value to the parameter. For instance, passing an int value to a function makes a copy of the int, and passing a pointer value makes a copy of the pointer, but not the data it points to.
Should I define methods on values or pointers?
func (s *MyStruct) pointerMethod() { } // method on pointer func (s MyStruct) valueMethod() { } // method on value
For programmers unaccustomed to pointers, the distinction between these two examples can be confusing, but the situation is actually very simple. When defining a method on a type, the receiver (s in the above examples) behaves exactly as if it were an argument to the method. Whether to define the receiver as a value or as a pointer is the same question, then, as whether a function argument should be a value or a pointer. There are several considerations.
First, and most important, does the method need to modify the receiver? If it does, the receiver must be a pointer. (Slices and maps act as references, so their story is a little more subtle, but for instance to change the length of a slice in a method the receiver must still be a pointer.) In the examples above, if pointerMethod modifies the fields of s, the caller will see those changes, but valueMethod is called with a copy of the caller's argument (that's the definition of passing a value), so changes it makes will be invisible to the caller.
By the way, pointer receivers are identical to the situation in Java, although in Java the pointers are hidden under the covers; it's Go's value receivers that are unusual.
Second is the consideration of efficiency. If the receiver is large, a big struct for instance, it will be much cheaper to use a pointer receiver.
Next is consistency. If some of the methods of the type must have pointer receivers, the rest should too, so the method set is consistent regardless of how the type is used. See the section on method sets for details.
For types such as basic types, slices, and small structs, a value receiver is very cheap so unless the semantics of the method requires a pointer, a value receiver is efficient and clear.
你的函数需要接收一个指针来增加值,这样你就不会传递结构的副本,并且在下一次迭代中 I 可以增加。
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)
}