🐕

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

2021/12/01に公開約2,400字

概要

自分が理解に苦しんだポインタについてまとめます。
エンジニアに転職してGoを書き始めた頃に、ポインタとアドレスの理解に苦しんだので「自分と同じ状況で困ってる方もいるのでは?」と思い、記事に残すことにしました。(転職前はRubyを書いていて、ポインタとアドレスの概念を知りませんでした)
何か誤っている内容がありましたら、ご教授いただけると幸いです。

この記事で伝えたいこと(ゴール)

  • この記事を読むことで、ポインタの概念をある程度、理解できる様になる。
  • Goでどういった時に、ポインタを使うかわかるようになる。

前提知識

ポインタを理解する為には、前提知識が必要なので、説明していきます。

静的型付け言語の実行の流れ

静的型付け言語で書かれたコード(Go、Java等)は、そのままではPCで実行できないので、ソースコードをコンパイラでコンパイルし、コンピュータがプログラムを実行できるような形にします。
この「実行できるような形」は、バイナリーコードになった実行ファイルです。
バイナリコードとは、コンピューターに処理を依頼する命令を、CPUが理解できるように2進数で表したコードのことです。

変数・メモリ・アドレス

ポインタを理解する前に、変数とメモリとアドレスについても理解する必要があるので、説明します。

  • メモリは、1バイト毎に番号がつけられ、区別されています。
  • 変数は実行ファイルになると、番号が割り当てられます。
  • 変数は、メモリ上の該当の番号の区分に格納され、記憶されます。
  • この変数に付与されるメモリの区分番号がアドレス(16進数)です。

Goで書くと

func main() {
	// メモリ○○番地に変数hogeを格納
	hoge := "テストです"
}

になります。
アドレスについては、次のポインタで説明します。

ポインタ

それでは、本題のポインタについて、説明します。

ポインタとは?

  • ポインタとは、メモリのアドレス情報のことです。
  • 前提知識でお伝えした、アドレス(16進数)のことです。

変数のアドレスを取得する

  • Goでは&を使って、変数のアドレスを取得することができます。
  • 例えば&hogeで変数hogeのアドレスを取得することができます。

ポインタ型変数

  • ポインタ型で宣言された変数で、メモリ上のアドレスを値として入れられます。
  • *を変数定義の際に型の前につけると、ポインタ変数として定義できます。
func main() {
	// string型の変数hoge
	var hoge string = "テストです"
	
	// string型へのポインタ型pointer変数に、hogeのアドレスを格納
	var pointer *string = &hoge 

	// "テストです" が出力される
	fmt.Println(hoge)
	
	// アドレス "0x000096210" が出力される
	fmt.Println(pointer)
}

出力結果

テストです
0xc000096210

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

  • ポインタ型変数名の前に*をつけることで、変数の中身へのアクセスができます。
  • 例えば、*pointerと書くと、pointerの値の参照ができます。
func main() {
    var hoge string = "テストです"
    var pointer *string = &hoge
    
    // "テストです" が出力される
    fmt.Println(*pointer)
}

出力結果

テストです

ポインタはGoでいつ使うのか

  • ポインタについて、「ざっくり理解はできたけど、どういった時に使うのか分からん...」といった疑問が自分は出てきたので、どんな時にGoのポインタ使うのか説明します。
  • Goでは、引数やレシーバを関数内で書き換える必要がある場合、書き換える対象はポインタで渡す必要があります。
package main

import "fmt"

type Hoge struct{ value int }

// ポインタ型ではないので、h.valueを書き換えられない
func (h Hoge) HogeA(v int) {
	h.value = v + 5
}

// ポインタ型なので、h.valueを書き換えられる
func (h *Hoge) HogeB(v int) {
	h.value = v + 5
}

func main() {
	var h Hoge

	h.HogeA(0)
	// 0のまま出力される
	fmt.Printf("HogeA(ポインタ型ではない)の出力結果:%v\n", h.value)

	h.HogeB(0)
	// +5された値が出力される
	fmt.Printf("HogeB(ポインタ型である)の出力結果:%v\n", h.value)
}

出力結果

HogeA(ポインタ型ではない)の出力結果:0
HogeB(ポインタ型である)の出力結果:5

まとめ

いかがでしたでしょうか。
ポインタの取り扱いに困っている初心者の方のお役に少しでも立てば嬉しく思います。
何か誤っている内容がありましたら、ご教授いただけると幸いです。
閲覧いただき、ありがとうございました。

Discussion

ログインするとコメントできます