Go言語ポインタまとめ【備忘録】

はじめに

Go言語の文法について調べていたら、ポインタというものが気になりました。
そこで今回の記事ではGo言語のポインタについて、
備忘録を兼ねて簡単にまとめていきたいと思います。

事前知識

事前知識として、変数メモリアドレスの関係について以下にまとめていきたいと思います。

  • コンピュータにはメモリと呼ばれるデータを記憶する領域がある
  • メモリは1バイト毎に番号が付けられ、区別されている
  • メモリに付けられている番号がアドレスとなる
  • 変数のデータはメモリ上に格納され、データが保持される

要はデータを保管するための箱がメモリであり、
箱を区別するための一意の番号がアドレス、
箱の中身が変数のデータといった具合です。

ポインタについて

ポインタとは

それでは、本題のポインタについて説明していきます。
単刀直入に言うと、ポインタはメモリのアドレスのことを指します。
アドレスは16進数で表されます。

ポインタの取得方法

  • Go言語では変数の前に&をつけることで、変数のポインタを取得することができます。
  • 取得したポインタを変数へ代入するには、ポインタ型変数を使用する必要があります。
  • *を変数宣言時の型の前に付けることでポインタ型変数の宣言が可能です。
package main
import "fmt"

func main() {
    //   int型変数ageに20を代入
    var age int = 20
    //   int型へのポインタ型変数pointerに変数ageのアドレスを代入
    var pointer *int = &age
}

ポインタ型変数の中身へのアクセス方法

  • ポインタ型変数の前に*を付けることで、ポインタ型変数の中身にアクセス可能です。
  • *を付けずに出力すると、ポインタ(アドレス)が出力されます。
package main
import "fmt"

func main() {
    //  int型変数ageに20を代入
    var age int = 20
    //  int型へのポインタ型変数pointerに変数ageのアドレスを代入
    var pointer *int = &age
    //  20が出力される
    fmt.Println(*pointer)
    //  0xc0000a0000が出力される
    fmt.Println(pointer)
}

出力結果

20
0xc0000a2000

ポインタ型引数

  • ポインタ型は引数に使用することも可能です。
package main
import "fmt"

func morningToNight(arg *string){
    *arg = "こんばんは"
}

func main(){
    //  string型変数messageに"おはよう"を代入
    var message string = "おはよう"
    //  messageの中身"おはよう"が出力される
    fmt.Println(message)
    //  messageのポインタを引数に渡し、メッセージを変更
    morningToNight(&message)
    //  messageの中身"こんばんは"が出力される
    fmt.Println(message)
}

出力結果

おはよう
こんばんは

ポインタと構造体について

構造体のポインタ

  • int型やstring型といったプリミティブ型と同様に、
    構造体もポインタ型を使用することができます。
package main
import "fmt"

type Person struct {
    name string
    age int
} 

func incremerntAge(arg *Person){
    arg.age++
}

func main(){
    student := Person{
        name: "山田太郎",
        age: 20,
    }
    
    //  {山田太郎 20}と出力される
    fmt.Println(student)
    //  構造体のポインタを渡し、年齢を変更する
    incremerntAge(&student)
    //  {山田太郎 21}と出力される
    fmt.Println(student)
}

出力結果

{山田太郎 20}
{山田太郎 21}

studentのageの値が変更されましたね。

値渡しと参照渡し

ここまでポインタについてまとめてきました。
ここからは値渡しと参照渡しについて少し触れておきたいと思います。

  • 値渡しは、変数の値(実体)をコピーして渡す方法です。
  • 参照渡しは、変数のアドレスを渡す方法です。
package main
import "fmt"

type Person struct {
    name string
    age int
} 

func incremerntAge1(arg Person){
    arg.age++
}

func incremerntAge2(arg *Person){
    arg.age++
}

func main(){
    
    student := Person{
        name: "山田太郎",
        age: 20,
    }
    
    //  studentの値のコピーを渡す
    incremerntAge1(student)
    //  値をコピーして渡しているだけなので、student.ageの値は変わらない
    fmt.Println(student)
    
    //  studentのポインタを渡す
    incremerntAge2(&student)
    //  studentの実体を渡しているので、student.ageの値が変わる
    fmt.Println(student)
}

出力結果

{山田太郎 20}
{山田太郎 21}

値渡しは変数の値をコピーして渡しているだけなので、
studentの実体へアクセスすることはできません。
一方、参照渡しはstudentのポインタを渡しているため、
ポインタを通じてstudentの実体へアクセスすることができます。

※参考

Go言語では、レシーバと呼ばれる機構をもつ関数(メソッド)を作ることができます。
このレシーバをもつ関数(メソッド)にも値渡しと参照渡しがあるため、
使用する際には注意が必要です。

package main
import "fmt"

type Person struct {
    name string
    age int
} 

func (p Person) changeAge1(arg int){
    p.age = arg
}

func (p *Person) changeAge2(arg int){
    p.age = arg
}

func main(){
    
    student := Person{
        name: "山田太郎",
        age: 20,
    }
    
    //  studentの値のコピー渡す
    student.changeAge1(18)
    //  値をコピーして渡しているだけなので、student.ageの値は変わらない
    fmt.Println(student)
    
    //  studentのポインタを渡す
    student.changeAge2(18)
    //  studentの実体を渡しているので、student.ageの値が変わる
    fmt.Println(student)
}

出力結果

{山田太郎 20}
{山田太郎 18}

終わりに

いかがだったでしょうか?
今回の記事ではGo言語のポインタについてまとめてみました。
この記事を読んでポインタについて少しでも理解を深めていただければ幸いです。

参考文献

【Go】自分が理解に苦しんだ、ポインタとアドレスについてまとめる
Goで学ぶポインタとアドレス