英文:
Pointer newbie - Correct me
问题
我还在学习Go语言,需要帮助来理清思路。
以下程序在每个Println
中输出Power
值为1
。我期望第一个输出为1
,第二个输出为2
。
我的假设是Change
函数用一个新地址覆盖了s
的地址,并且这个改变会反映到调用者(main
函数)中。在这种情况下,当第二个Println
调用时,原始地址应该指向新创建的地址。
我的假设是错误的,但我无法弄清楚为什么。
package main
import (
"fmt"
)
type Pod struct{
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(pod)
fmt.Println(pod.Power)
}
func Change(s *Pod) {
s = &Pod{2}
}
为了进一步探索发生了什么,我尝试打印地址,结果如下:
import (
"fmt"
)
type Pod struct{
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(&pod) //0xc04202c020
Change(pod)
fmt.Println(&pod) //0xc04202c020
}
func Change(s *Pod) {
fmt.Println(&s) //0xc04202c030(我期望这里是0xc04202c020)
s = &Pod{2}
fmt.Println(&s) //0xc04202c030
}
英文:
I am still learning go and need a hand to clear my head.
Following program output Power
value as 1
in each Println
. I was expecting 1
as first output and 2
as second output.
My assumption was the Change
func
overwrite the address of
s
with a new address
and this change will reflect back to the caller (main func
). In such case, the original address would be pointing to newly created address when it call the second Println
.
My assumption is wrong but I can't figure out why.
package main
import (
"fmt"
)
type Pod struct{
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(pod)
fmt.Println(pod.Power)
}
func Change(s *Pod) {
s = &Pod{2}
}
To explore further on what happens under cover, I did tried to print addresses this time and it looks like below;
import (
"fmt"
)
type Pod struct{
Power int
}
func main() {
pod := &Pod{ 1}
fmt.Println(&pod) //0xc04202c020
Change(pod)
fmt.Println(&pod) //0xc04202c020
}
func Change(s *Pod) {
fmt.Println(&s) //0xc04202c030 ( I was expecting 0xc04202c020 here)
s = &Pod{ 2}
fmt.Println(&s) //0xc04202c030
}
答案1
得分: 4
这是因为当你将参数传递给函数时,它们总是按值传递的。
换句话说,即使在函数参数中接受一个指针,结构体的地址也会被复制。
当你尝试给s
赋值来改变地址时,它只会改变本地副本,而不是函数外部的那个。
要在函数内部改变地址,你需要传入一个指针指针,然后对解引用的s
进行赋值,例如:
package main
import (
"fmt"
)
type Pod struct {
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(&pod)
fmt.Println(pod.Power)
}
func Change(s **Pod) {
*s = &Pod{2}
}
在这种情况下,函数仍然传递了一个地址的副本,但因为它是指向指针的指针,所以当你将s
解引用为*s
时,你将得到函数外部结构体的地址。这意味着如果你对*s
进行赋值,你可以改变函数外部指针的地址。
当然,就像Andy Schweig所说的那样,你可能真的不想这样做,而是使用普通指针版本的函数根据需要更改各个字段。
package main
import (
"fmt"
)
type Pod struct {
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(pod)
fmt.Println(pod.Power)
}
func Change(s *Pod) {
s.Power = 2
}
这个例子可以工作,因为当你输入s.Power = 2
时,Go实际上会为你执行类似(*s).Power = 2
的操作。所以它会自动解引用s
,给你一个实际的Pod
结构体来使用。
在这个普通指针的例子中,你不能执行*s = &Pod{2}
,因为在这种情况下,*s
实际上将等于类型Pod
,而不是*Pod
。
因此,如果你想使用&Pod{2}
语法来分配一个地址,你需要传递一个指向指针的指针。在s **Pod
的情况下,解引用的*s
将指向Pod
的地址,而不是实际的Pod
,所以*s
的类型将是*Pod
,允许你分配&Pod{2}
。
话虽如此,只有当你想要使用&Pod{2}
语法来分配一个地址时,才需要**Pod
。
如果你不需要使用&Pod{2}
语法,你可以直接解引用s
并使用普通的Pod{2}
进行赋值。
package main
import (
"fmt"
)
type Pod struct {
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(pod)
fmt.Println(pod.Power)
}
func Change(s *Pod) {
*s = Pod{2}
}
这也可以工作,因为现在s
是一个地址的副本,当你解引用它时,你得到的是函数外部的值,它的类型是Pod
,而不是*Pod
。
这意味着你可以通过去掉&
来对它进行赋值。
基本上,如果你使用&
,它意味着你想要分配一个地址,而不是实际的值。
希望这个解释不会太令人困惑。我使用**Pod
进行解释,因为我以为你想要使用&Pod{2}
语法,而不是Pod{2}
,但如果不是这样的话,我的s.Power = 2
或*s = Pod{2}
的例子可能更有意义。
英文:
It's because when you pass arguments to functions etc, they are always passed by value.
In other words, even though you are accepting a pointer in the function parameter, the address of the struct will be copied.
Then when you assign to s
to try and change the address it only changes that local copy, not the one outside the function.
To change the address from within the function you would need to pass in a pointer pointer, then assign to the dereferenced s
, for example:
package main
import (
"fmt"
)
type Pod struct {
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(&pod)
fmt.Println(pod.Power)
}
func Change(s **Pod) {
*s = &Pod{2}
}
In this case a copy of an address is still being passed into the function, but because it's a pointer to a pointer it means that when you dereference s
as *s
you will get the address of the struct outside of the function. This means if you assign to *s
you can change the address of the pointer outside the function.
Of course, like Andy Schweig says, you probably wouldn't really want to do that though, and would probably just change individual fields as needed with the normal pointer version of the function.
package main
import (
"fmt"
)
type Pod struct {
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(pod)
fmt.Println(pod.Power)
}
func Change(s *Pod) {
s.Power = 2
}
This works because when you type s.Power = 2
Go will actually do something like (*s).Power = 2
for you. So it automatically dereferences s
for you which gives you the actual Pod
struct to work with.
You can't do *s = &Pod{2}
in this normal pointer example because in that case *s
will actually equal type Pod
, not *Pod
.
Because of that if you want to use the &Pod{2}
syntax to assign an address you need to pass a pointer to the pointer. In the case of s **Pod
the dereferenced *s
will point to the address of the Pod
instead of the actual Pod
, so *s
will be of type *Pod
which allows you to assign &Pod{2}
.
Having said all of that, a **Pod
is only required if you want to assign an address with the &Pod{2}
syntax.
If you don't need to use the &Pod{2}
syntax you can just dereference s
and assign with a normal Pod{2}
.
package main
import (
"fmt"
)
type Pod struct {
Power int
}
func main() {
pod := &Pod{1}
fmt.Println(pod.Power)
Change(pod)
fmt.Println(pod.Power)
}
func Change(s *Pod) {
*s = Pod{2}
}
This also works, because now s
is a copy of an address, and when you dereference you get to the value outside the function which is type Pod
, not *Pod
.
That means you can just assign to it by removing the &
.
Basically if you use &
it means you want to assign an address rather than the actual value.
I hope this explanation isn't too confusing. I explained using a **Pod
because I thought you wanted to use the &Pod{2}
syntax rather than Pod{2}
, but if that's not the case then my s.Power = 2
or *s = Pod{2}
examples may make more sense.
答案2
得分: 2
将函数内部参数的值更改为函数本身永远不会对调用者传递的参数产生影响。所有参数都是按值传递的。如果你想在指向s
所指向的对象内部更改Power
的值(在调用者中为pod
),可以使用s.Power = 2
。如果你真的想将调用者中的指针变量设置为不同的Pod
对象,你需要将Change
函数的参数声明为s **pos
,将函数中的赋值改为*s = &Pod{2}
,并调用Change(&pod)
。不过,这可能不是你想要做的。
英文:
Changing the value of a parameter to a function inside the function never has an effect on the parameter passed by the caller. All parameters are passed by value. If you want to change the value of Power
inside the object pointed to by s
(pod
in the caller), use s.Power = 2
. If you actually want to set the pointer variable in the caller to a different Pod
object, you need to declare the parameter to Change
as s **pos
, change the assignment in the function to *s = &Pod{2}
, and call Change(&pod)
. That's probably not what you want to do, though.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论