Closed5

[golang] 値レシーバとポインターレシーバの備忘録

shiratorishiratori
  • ポインタはメモリアドレスが保存される変数のこと
  • 型の前に置かれた*はそれがポインタ型であることを示す
  • 変数の前に*をおくと、その変数が指し示す値がデリファレンスされる
  • &アンパサンドは変数の値のアドレスを取得するための演算子
shiratorishiratori
name := "hoge"
var namePointer *string
namePointer = &name

name := "hoge"
namePointer := &name

は同じことをやっている。

shiratorishiratori

アンパサンドとアスタリスクとデリファレンス

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
shiratorishiratori

構造体はリファレンス、デリファレンスをしなくても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}
shiratorishiratori

値レシーバーとポインタレシーバの挙動を確認してみる。

  • 値レシーバーの場合, 呼び出し元データのコピーが入るので元の値は変更されない。
  • ポインタレシーバは呼び出し元データのアドレスが渡されるので元のデータが書き変わる。
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にクローズされました