対象読者
Go言語の文法を学んでる段階の方から、*とか&を使うケースは覚えたけど何となく使ってしまってる方まで(ポインタ渡しについては触れてません)
はじめに
ポインタ変数を理解するに当たって、そもそも変数が定義された時どのようなことが行われているのかを理解する必要があります。そのために必要なメモリというものを冒頭で説明したいと思います。
#メモリとは
PCのCPUが処理を行う際に読み書きを行う値(データ)を格納する場所のことです。メモリの最小単位は1バイトであり、1バイトごとにアドレスと呼ばれる識別番号が16進数で割振られています。
ここからはイメージを掴むために画像を入れながら説明します。16進数の適当なアドレスを用いて、メモリを表すとこのような形になります。
変数定義の流れ
では実際に変数が定義された際の流れを見ていきましょう。
Int型のaという変数を定義したとします。このaという変数が定義された時メモリは以下のようになります。
このように変数の型やサイズに合わせてメモリの中のアドレスが確保されるのです。
* アドレスと変数のイメージを掴むことが目的なので、変数に対応した厳密な16進数ではありませんがご理解ください。
ポインタとは
ここまで変数定義された時の一連の流れを見てきました。
では本題のポインタの説明をします。
ポインタを端的に言うと、「その変数の型情報や大きさを指し示したもの」です。
より具体的には、その変数が格納されてるメモリ領域の先頭アドレスを指します。この先頭アドレスに変数の型に関する情報や領域(大きさ)に関する情報が入っているのです。
この図において変数aのポインタは75bd5b9ということになり、ここには型や領域に関する情報が入っています。
Goでの実装例
ではこれらのポインタをgoにおいてどのように扱うか見ていきましょう。
変数からポインタを出力する
簡単な実装例として、変数のポインタを出力してみます
①変数から直接取得する場合
package main
import "fmt"
func main() {
var a int
fmt.Println(&a)
// 出力結果
// 0xc000094000
}
これは&で(アンパサド演算子)変数のポインタを出力したものです。
####②ポインタ型を指定して参照する場合
ポインタ型変数には変数のアドレス値が入ります。
よって、先ほどの変数aのアドレス値をポインタ型変数bに代入すれ変数のポインタを出力できます
package main
import "fmt"
func main() {
var a int
var b *int
b = &a
fmt.Println(b)
// 出力結果
// 0xc000094000
}
こちらは予め定義したポインタ型変数に①で定義した変数aのアドレスを代入したものになります。出力方法は違いますが、ここでの値は等しくなります。
ここで使った特殊記号は以下の2つです。
*
ポインタ型変数の定義
(例 var a *int
var b *string
&
変数のアドレスを取得
③ポインタから変数の値を取得する場合
ポインタ型の指定と同じなので紛らわしいですが、以下のようにします。
package main
import "fmt"
func main() {
var a int
var b *int
b = &a
c := *b
fmt.Println(c)
// 出力結果
// 0
}
出力した変数cには何も値が入っていないので0が出力されました。
goの文法を理解する上で避けては通れないポインタですが、基本概念から落とし込み手を動かすことで徐々に慣れていきましょう