2016-06-29 8 views
2

ポインタメソッドの受信者と非ポインタメソッドの受信者について私が理解しているのは、最初にメソッドで変更することができ、次のメソッドは変更できません。golang:ポインタメソッドレシーバの再割り当て

それで、以下は私が期待した通りに正確に働いた。

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および未知/ 0

しかし、私はを一度に置き換えて再割り当てしたいと思います。ですから、私はちょうど以下のようにupdateメソッドを変更しました。

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

そして、それは変更されない主な方法とプリントダブルhongseok/13

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

上記の変更で問題が解決しました。

意味の違いはないと思います。私は何が欠けていますか?

答えて

14

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

あなたが新しいstudent値にsに全く新しい「ポインタ値」、及び新しい*sポイントを割り当てます。変数sはメソッド本文にのみスコープが設定されているため、この戻り値の後に副作用はありません。

2番目の例では

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

あなたはsを参照解除し、新しいstudent値を指すように、または別々にそれを置くために*sの値を変更するには、アドレスに新しいstudent値を入れているされていますここで、sがポイントです。

0

この例では、sに格納されているアドレスを別の値に変更しています。

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

ポインタは「参照渡し」とみなされますが、参照自体は呼び出しスタックにプッシュされる他の値と同じです。メインに戻ると、sの値は、そのスコープにあったものと同じです。具体的には、s = 1というメイン(簡潔にするためにアドレス1と2を呼び出す)を呼び出し、studentをアドレス2に割り当て、sのバージョンをポップしたときにs = 2に設定する方法では、スタックとsは、1を指し、変更されていません。

この後者の例では、

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

あなたはsを逆参照し、その場所に新しいオブジェクトを割り当て、既存のメモリを上書きしています。メインにポインタを戻すと、同じ位置を指していますが、メモリ内のその場所に異なるデータがあります。したがって、この例ではstudentという新しいインスタンスを作成して1という名前を付けていますので、呼び出し元のスコープに新しい値が返ってきたときに返します。最初の例で

0

あなたの主な問題は、あなたの質問に現れる2つの概念のどちらもうまく理解していないということです。

ポインタで開始しましょう。ポインタを使用しない場合、値の割り当てとは、以前の値の単純なコピーを作成することを意味します。新しい価値は、以前のものと縛られていません。つまり、古い値または新しい値を変更すると、それは2番目の値に影響しません。これは、プリミティブ型(int、bool、stringなど)と構造体の場合は正常です。

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

ここでポインタは、メモリにいくらかのスペースを指し示すものです。簡単にするために、2つのポインタを作成し、両方とも同じ場所を指します。

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 
} 

そして、混乱しないでください。ポインタはvalueという名前のポイントを持つこともできます。

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とメソッドのコピーが呼び出されることを意味します。もちろん、コピーの価値を変えることはできますが、元の価値には影響しません。