Go语言 在指针方法接收器中重新赋值

hrysbysz  于 11个月前  发布在  Go
关注(0)|答案(3)|浏览(119)

我对指针方法接收器和非指针方法接收器的理解是,第一个可以在方法中修改,第二个不能。
所以,下面的工作完全符合我的预期。

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/13unknown/0
但是,我想把update方法中的所有s都替换为reassigning。所以,我把update方法修改如下。

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


它不会改变main方法中的s,并打印双hongseok/13

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


以上的改变解决了这个问题。
我觉得语义上没什么区别。我错过了什么?

7dl7o3gd

7dl7o3gd1#

在第一个例子中:

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指向的地址处。

mwkjh3gx

mwkjh3gx2#

在本例中,您将存储在s中的地址更改为不同的值;

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

字符串
虽然使用指针被认为是“通过引用传递”,但引用本身是一个像任何其他值一样被推送到调用堆栈上的值。当您返回main时,s的值是该范围内的任何值。因此,为了给予更具体的内容,您使用s = 1调用main(为简单起见,调用地址1和2),在方法中,您分配位于地址2的新student并设置s = 2,当你返回时,s的版本从堆栈中弹出,main中的s指向1,它没有改变。
在后一个例子中;

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


你正在解引用s并将一个新的对象分配到该位置,重新分配现有的内存。当你返回时,main中的指针仍然指向同一个位置,但在内存中的该位置有不同的数据。因此在这个例子中,你正在写入一个新的student示例,以地址1,所以当你返回时,你会在调用范围中看到新的值。

dxpyg8gm

dxpyg8gm3#

我想,你的主要问题是你的问题中出现的两个概念你都不太理解。
让我们从指针开始。当你不使用指针时,赋值意味着创建一个前一个值的简单副本。新值与前一个值没有任何关系。这意味着如果你改变旧值或新值,它不会影响第二个值。这对于基本类型(如int,bool,string)和结构是正常的。

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).age or p2.age 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


现在回到方法,接收器是/不是指针。如果方法的接收器是指针,这意味着你可以改变它的值--就像我几行前做的一样。
如果方法接收器不是指针,这意味着-在调用一个方法之前,它将被创建一个结构体的副本,方法将在该副本上被调用。当然,你可以改变副本的值,但它不会影响原始值。

相关问题