golang:重新分配指针方法接收器

时间:2016-06-29 16:42:28

标签: go

我对指针方法接收器和非指针方法接收器的理解首先可以在方法中修改,然后下一个不是。

所以,以下工作完全按照我的预期进行。

type student struct {
    name string
    age  int
}

func (s *student) update() {
    s.name = "unknown"
    s.age = 0
}

func main() {
    s := student{"hongseok", 13}
    fmt.Println(s)

    s.update()
    fmt.Println(s)
}

打印 hongseok / 13 unknown / 0

但是,我想通过重新分配立即替换update方法中的整个 s 。所以,我刚刚修改了更新方法。

func (s *student) update() {
    s = &student{"unknown", 0}
}

它并没有改变主方法中的 s 并打印双 hongseok / 13

func (s *student) update() {
    *s = student{"unknown", 0}
}

以上更改解决了问题。

我认为没有语义差异。我错过了什么?

3 个答案:

答案 0 :(得分:21)

在第一个例子中:

func (s *student) update() {
    s = &student{"unknown", 0}
}

您正在为s分配一个全新的“指针值”,而新的*s指向新的student值。变量s仅限于方法体,因此返回后没有副作用。

在第二个例子中

func (s *student) update() {
    *s = student{"unknown", 0}
}

您正在取消引用s,并将*s的值更改为指向新的student值,或者换句话说,您正在添加新的student s指向的地址的值。

答案 1 :(得分:1)

在此示例中,您将s中存储的地址更改为其他值;

func (s *student) update() {
    s = &student{"unknown", 0}
}

虽然使用指针被视为通过引用传递'引用本身是一个像推送到调用堆栈的任何其他值。当您返回main时,s的值是该范围内的值。因此,为了给出更具体的内容,您使用s = 1调用main(为简单起见调用地址1和2),在方法中分配位于地址2的新student并设置{{1} },当您返回时,从堆栈中弹出s = 2版本,并且主要点中的s不变为s

在后一个例子中;

1

您解除引用func (s *student) update() { *s = student{"unknown", 0} } 并将新对象分配给该位置,覆盖现有内存。当您返回main中的指针仍指向同一位置但您在内存中的该位置有不同的数据。因此,在此示例中,您正在编写新的s实例以解决student,因此当您返回时,您会在调用范围中看到新值。

答案 2 :(得分:-4)

我认为,你的主要问题是你不能很好地理解你问题中出现的两个概念中的一个。

让我们从指针开始。当您不使用指针时,分配值意味着创建以前值的简单副本。新值不受前一个约束。这意味着如果您更改旧值或新值,则不会影响第二个值。对于原始类型(如整数,bool,字符串)和结构,这是正常的。

a := 1
b := a
a++   // a was changed, but value of b does not change at all

现在指针 - 它指向一些存储空间。为简单起见,我们创建了两个指针,两者都指向同一个地方。

package main

import (
    "fmt"
)

func main() {
    type student struct {
        name string
        age  int
    }
    p1 := &student{"hongseok", 13} // this mean create a new student, but we want only address of student, not value
    p2 := p1
    (*p1).age = 15      // because p1 and p2 point to same memory (*p2).age also changed
    fmt.Println("1.", p2.age) // 15
    p2.age = 32         // in golang I could write (*p2).ago or p2.ago this is the same
    fmt.Println("2.", p1.age) // 32
    fmt.Println("3.",p1, "==", p2)
    fmt.Printf("4. %p == %p\n", p1, p2)
    // But now I force point p2 to new place
    p2 = &student{"another student", 12}
    fmt.Println("5.", p1, "!=", p2)
    fmt.Printf("6. %p == %p\n", p1, p2)
    p1.age = 14    // does it influce p2.age? no! Why? Because it is in different address
    fmt.Println("7.", p1, "!=", p2)
        // but could is somehow force that point to same address ? Of course
    p2 = p1 // p2 will point to same place as p1 and old value of p2 will be freed by garbage collector 
}

并且不要混淆 - 指针也可以指向命名值。

a := 42  // means allocate memory for integer, and we give that memory name "a" 
p := &a
*p++     // it change value of a
a = 5    // and of course this change value of *p

现在回到方法,接收器是/不是指针。如果方法的接收者是指针,那意味着你可以改变它的值 - 就像我几行之前一样。

如果方法接收器不是指针,那意味着 - 在调用方法之前,将创建一个struct的副本,并且将在该副本上调用方法。您当然可以更改副本的值,但它不会影响原始值。