【问题标题】:Pointer newbie - Correct me指针新手 - 纠正我
【发布时间】:2016-11-23 09:28:17
【问题描述】:

我还在学习围棋,需要帮助我清醒一下。

在每个Println 中,以下程序输出Power 值为1。我期待1 作为第一个输出,2 作为第二个输出。 我的假设是 Change funcnew address 覆盖 address of s 并且此更改将反映给调用者 (main func)。在这种情况下,当调用第二个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}
}

Code

为了进一步了解幕后发生的事情,我这次确实尝试打印地址,如下所示;

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
}

【问题讨论】:

    标签: go


    【解决方案1】:

    这是因为当您将参数传递给函数等时,它们总是按值传递。

    换句话说,即使你在函数参数中接受一个指针,结构体的地址也会被复制。

    然后,当您分配给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} 示例可能更有意义。

    【讨论】:

    • 谢谢你,解释得很好。
    • @Nair 我看到你之前的评论现在已经消失了,但正因为如此,我只是编辑了答案以提供更多信息。希望对你有帮助:)
    • 是的。这与指针取消引用有关,我暂时忘记了它,现在回忆起我在大学里学到的东西。一段时间以来,我们一直在减少使用所有新一代语言的指针。无视我。
    【解决方案2】:

    将参数的值更改为函数内部的函数永远不会对调用者传递的参数产生影响。所有参数都按值传递。如果要更改s(调用者中的pod)指向的对象内的Power 的值,请使用s.Power = 2。如果真的想将调用者中的指针变量设置为不同的Pod对象,则需要将Change的参数声明为s **pos,将函数中的赋值改为*s = &Pod{2},调用@987654329 @。不过,这可能不是您想要做的。

    【讨论】:

    • 感谢您的评论。
    猜你喜欢
    • 2021-07-28
    • 2013-04-22
    • 2015-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-28
    • 2014-06-17
    相关资源
    最近更新 更多