【第八回Go言語学習備忘録】ポインタを使ってみた!

2月 12, 2022

どもです。キースヘリングのリュックで出勤しているせいか社内で派手派手くんと呼ばれ始めたsaisaiです。


今回はポインタについて学んだことを備忘録として残していきたいと思います。


このあたりからいよいよ学習内容が難しくなってきています…。言語初学者が大体配最初につまづくのがポインタなのかなと思いながら学習を進めていました。


仕組みを知っただけでは考え方がとても抽象的なので、やはりポインタに関してもどんどんコードを作成しながら体にしみ込ませるのが良さそうです。


では、早速やっていきたいと思います。

ポインタとアドレスってなんぞや!

ポインタを理解する上でまず必要となる知識はずばり、"メモリ"と"アドレス"でしょう。


例えば、何かの何か変数に値を代入したとします。この変数の値はコードをコンパイルした時にメモリ内のどこかに格納され、その位置は16進数で定めれます。この格納されるメモリの位置を"アドレス"といいます。


上記の前提知識を踏まえた上で簡単なサンプルコードを作成し、理解を深めたいと思います。
今回作成したコードは以下の通りです。

package main

import (
	"fmt"
)

func main() {
	name := "saisai"
	address := &name
	fmt.Printf("%T\n", address) //*string
	fmt.Println(address) //0xc000010200(アドレス)
	fmt.Println(*address) //saisai
}


まずは下記の部分に注目していきます。

name := "saisai"
	address := &name

一行目はこれまでやってきたように変数nameに文字列を代入するという極めてシンプルなコードです。
では、2行目はどうでしょうか。

一行目で代入した変数に"&"を使用することで"ポインタ型"を作成することができます。ポインタ型とは"*型"で定義され、そのメモリ上のアドレスと格納されている値を記憶する型です。
例えば、以下をご覧ください。

fmt.Printf("%T\n", address) //*string

こちらは"&name"を代入した変数"address"の方を示しています。上記のように"*string"というようにポインタ方は定義されます。つまり変数"address"には"変数nameが格納されているメモリのアドレス(string型)"という情報が代入されています。この"address"のような変数をポインタ変数といいます。例えば以下のように

fmt.Println(address) //0xc000010200

そのまま"address"を出力すると、メモリのアドレスが表示されるというわけです。しかし、このアドレスとはもともと"&name"により生成されており、変数"name"には値が代入されていたはずですから、当然このアドレスには最初に代入された値があるはずです。では、アドレスに格納された値を取り出してみましょう。

fmt.Println(*address) //saisai

上記のように"*変数"として出力すると、ポインタ変数のアドレスではなく格納されている値を出力することができます。
ちなみにこのアドレス上に存在する値は、書き換えることが可能です。

package main

import (
	"fmt"
)

func main() {
	name := "saisai"
	fmt.Println(name) //saisai

	address := &name
	*address = "saisaisai"
	fmt.Println(name) //saisaisai
}

“*address"もまた変数であることに注意すれば、理解しやすいです。"*address"には"変数nameが格納されているメモリアドレス上の値"が代入されています(つまり変数nameの値)。なので"*address"を書き換えれば必然的に変数"name"の値が変わります。同じ変数"name"の出力結果でも、それぞれの値が違うのにはそういった仕組みがあるようです。

初期化されたポインタ変数

これまでは、特定の変数に値が入っている状態が前提でした。では、値を持たずにメモリのアドレスだけ確保したポインタ変数を定義することはできるのでしょうか。

以下のようなコードを作成してみました。

package main

import (
	"fmt"
)

func main() {
	num := new(int)
	fmt.Println(num) //0xc000016078(変数numのアドレスを表示)
	for *num = 0; *num < 10; *num++ {
		fmt.Println(*num)
	}
}
//0-9までの値を表示

上記のようにnew関数を使用することで初期化されたアドレスを用意してポインタ変数を生成できます。これで変数numのアドレス内に初期値(今回は0)を格納し、for文で値を1ずつ増やしていくという処理が可能となりました!


これまでスライスやマップを初期化して用意する場合はmake関数を使用していましたが、ポインタの場合はnew関数をしようするようですね。この辺りもしっかり識別していきたいところです。

ひとこと

本日はポインタについて学習したことをまとめました。ポインタはGo言語の学習要項のなかでもかなり奥が深く、まだまだ学び足りない気がしていますが、実際に動くコードを書いてどんどん定着させていきたいところです!


学習にかなり苦戦したポインタですが、今回の記事の執筆を通して"動作はなんとなくわかる"から"実際に使用できる"くらいまでには進歩できたので、やはりアウトプットは大事だなと再確認しました。


これからも学んだことをどんどん記事にしていきたいと思います。


ここまで読んでいただきありがとうございました!


-saisai-


↓本日のオススメ

Golang

Posted by CY