Closed5
[golang] 値レシーバとポインターレシーバの備忘録
- ポインタはメモリアドレスが保存される変数のこと
- 型の前に置かれた
*
はそれがポインタ型であることを示す - 変数の前に
*
をおくと、その変数が指し示す値がデリファレンスされる -
&
アンパサンドは変数の値のアドレスを取得するための演算子
name := "hoge"
var namePointer *string
namePointer = &name
と
name := "hoge"
namePointer := &name
は同じことをやっている。
アンパサンドとアスタリスクとデリファレンス
index := 1
fmt.Println("index: ", index)
fmt.Println("&index: ", &index)
address := &index
fmt.Println("address: ", address)
fmt.Println("*address: ", *address)
実行すると
↓
index: 1
&index: 0x1400012c008
address: 0x1400012c008
*address: 1
構造体はリファレンス、デリファレンスをしなくてもGoが自動的に判断してくれる。
数が引数の場合
func add(i *int) {
*i++ // デリファレンスが必要
}
func main() {
index := 1
add(&index)
fmt.Println("index", index)
}
実行すると
↓
index: 2
構造体が引数のとき
type person struct {
name string
age int
}
func (p *person) birthday() {
p.age++ // デリファレンスが不要。(*p).age++ としなくていい。
}
func main() {
tom := person{
name: "Tom",
age: 15,
}
fmt.Printf("%+v\n", tom)
// (&tom).birthday() としなくていい
// Goが自動的に「変数のアドレス(&)」だと判定する
tom.birthday()
fmt.Printf("%+v\n", tom)
}
実行すると
↓
{name:Tom age:15}
{name:Tom age:16}
値レシーバーとポインタレシーバの挙動を確認してみる。
- 値レシーバーの場合, 呼び出し元データのコピーが入るので元の値は変更されない。
- ポインタレシーバは呼び出し元データのアドレスが渡されるので元のデータが書き変わる。
main.go
package main
import "fmt"
type Person struct {
Name string
Age int
}
// 値レシーバ
func (p Person) setNameValue(name string) {
// 値レシーバの場合, pは呼び出し元のPersonデータのコピーが入る
p.Name = name
fmt.Println("値レシーバのpersonの値 :", p)
fmt.Printf("値レシーバのpersonのアドレス :\t%p\n", &p)
}
// ポインタレシーバ
func (p *Person) setNamePointer(name string) {
// ポインタレシーバの場合、pは呼び出し元のPersonデータのアドレスが入る
p.Name = name // 構造体のフィールドにアクセスするのに、その構造体をデリファレンスする必要がない
fmt.Printf("ポインタレシーバのpersonの値 :\t%p\n", p)
fmt.Printf("ポインタレシーバのpersonのアドレス :\t%p\n", &p)
}
func main() {
// オリジナルの構造体
originalPerson := Person{
Name: "none",
Age: 20,
}
fmt.Printf("\n")
fmt.Printf("---------------------------------------------\n")
fmt.Println("originalPersonの値 :", originalPerson)
fmt.Printf("originalPersonのアドレス :\t%p\n", &originalPerson)
// 値格納用の変数を作る
var personValue Person
// ポインター型の変数を作る
var personPointer *Person
/* オリジナルの構造体を値として personValue に代入
-------------------------------------------------------------- */
personValue = originalPerson
fmt.Printf("\n")
fmt.Printf("---------------------------------------------\n")
fmt.Printf("値として代入 personValue = originalPerson \n")
fmt.Printf("---------------------------------------------\n")
fmt.Println("変数personValueの値 :", personValue)
fmt.Printf("変数personValueのアドレス :\t%p\n", &personValue)
fmt.Printf("\n")
fmt.Printf("====> setNameValue(値レシーバ)personValue.setNameValue(\"Ken\") \n")
// 値レシーバの実行
personValue.setNameValue("Ken")
fmt.Println("変数personValueの値 :", personValue)
fmt.Printf("変数personValueのアドレス :\t%p\n", &personValue)
fmt.Println("originalPersonの値 :", originalPerson)
fmt.Printf("originalPersonのアドレス :\t%p\n", &originalPerson)
fmt.Printf("\n")
fmt.Printf("====> setNameValue(ポインタレシーバ)personValue.setNamePointer(\"Ken\") \n")
// ポインタレシーバの実行
personValue.setNamePointer("Ken")
fmt.Println("変数personValueの値 :", personValue)
fmt.Printf("変数personValueのアドレス :\t%p\n", &personValue)
fmt.Println("originalPersonの値 :", originalPerson)
fmt.Printf("originalPersonのアドレス :\t%p\n", &originalPerson)
/* オリジナルの構造体をポインターとして personValue に代入
-------------------------------------------------------------- */
personPointer = &originalPerson
fmt.Printf("\n")
fmt.Printf("---------------------------------------------\n")
fmt.Printf("ポインターとして代入 personPointer = &originalPerson \n")
fmt.Printf("---------------------------------------------\n")
fmt.Printf("変数personPointerの値 :\t%p\n", personPointer)
fmt.Printf("変数personPointerのアドレス :\t%p\n", &personPointer)
fmt.Printf("\n")
fmt.Printf("====> setNameValue(値レシーバ) personPointer.setNameValue(\"Tom\") \n")
// 値レシーバの実行
personPointer.setNameValue("Tom")
fmt.Printf("変数personPointerの値 :\t%p\n", personPointer)
fmt.Printf("変数personPointerのアドレス :\t%p\n", &personPointer)
fmt.Println("originalPersonの値 :", originalPerson)
fmt.Printf("originalPersonのアドレス :\t%p\n", &originalPerson)
fmt.Printf("\n")
fmt.Printf("====> setNamePointer(ポインタレシーバ) personPointer.setNamePointer(\"Tom\")\n")
// ポインタレシーバの実行
personPointer.setNamePointer("Tom")
fmt.Printf("変数personPointerの値 :\t%p\n", personPointer)
fmt.Printf("変数personPointerのアドレス :\t%p\n", &personPointer)
fmt.Println("originalPersonの値 :", originalPerson)
fmt.Printf("originalPersonのアドレス :\t%p\n", &originalPerson)
}
実行すると
↓
---------------------------------------------
originalPersonの値 : {none 20}
originalPersonのアドレス : 0x14000114018
---------------------------------------------
値として代入 personValue = originalPerson
---------------------------------------------
変数personValueの値 : {none 20}
変数personValueのアドレス : 0x14000114048
====> setNameValue(値レシーバー)personValue.setNameValue("Ken")
値レシーバーのpersonの値 : {Ken 20}
値レシーバーのpersonのアドレス : 0x14000114078
変数personValueの値 : {none 20}
変数personValueのアドレス : 0x14000114048
originalPersonの値 : {none 20}
originalPersonのアドレス : 0x14000114018
====> setNameValue(ポインタレシーバー)personValue.setNamePointer("Ken")
ポインタレシーバーのpersonの値 : 0x14000114048
ポインタレシーバーのpersonのアドレス : 0x1400011c028
変数personValueの値 : {Ken 20}
変数personValueのアドレス : 0x14000114048
originalPersonの値 : {none 20}
originalPersonのアドレス : 0x14000114018
---------------------------------------------
ポインターとして代入 personPointer = &originalPerson
---------------------------------------------
変数personPointerの値 : 0x14000114018
変数personPointerのアドレス : 0x1400011c020
====> setNameValue(値レシーバー) personPointer.setNameValue("Tom")
値レシーバーのpersonの値 : {Tom 20}
値レシーバーのpersonのアドレス : 0x14000114108
変数personPointerの値 : 0x14000114018
変数personPointerのアドレス : 0x1400011c020
originalPersonの値 : {none 20}
originalPersonのアドレス : 0x14000114018
====> setNamePointer(ポインタレシーバー) personPointer.setNamePointer("Tom")
ポインタレシーバーのpersonの値 : 0x14000114018
ポインタレシーバーのpersonのアドレス : 0x1400011c030
変数personPointerの値 : 0x14000114018
変数personPointerのアドレス : 0x1400011c020
originalPersonの値 : {Tom 20}
originalPersonのアドレス : 0x14000114018
このスクラップは2022/09/19にクローズされました