Go 言語のポインター

Go のアドレス演算子

Go では変数に & を付けることで、その変数のメモリ上のアドレスを取得できます。 アドレスというのはその名の通り、メモリ内での場所 (住所) のことです。

次の例では、3行目で変数 x のアドレスを表示しています。

var x int32 = 10
fmt.Println(" x = ", x)  //  x =  10
fmt.Println("&x = ", &x) // &x =  0xc000118000

実行時の表示は一例です。環境によって異なります。

変数のアドレスを取り出す &アドレス演算子 (address operator) といいます。

Go のポインター

Go ではアドレスを格納するための変数が用意されています。これを ポインターといいます。

ポインターはデータ型毎に、 int32 型のポインターの型は *int32、 byte 型のポインター型は *byte という具合に、 型名に * を付ける形で宣言できます。

次の例では、int32 型の変数 x のアドレス (&x) を、*int32 型のポインター変数 p に代入しています (6行目)。

var x int32 = 10
fmt.Println(" x = ", x)  //  x =  10
fmt.Println("&x = ", &x) // &x =  0xc000118000

var p *int32
p = &x
fmt.Println(" p = ", p)  //  p =  0xc000118000

ポインターの初期値は nil です。nil はどのアドレス情報も保持していない状態です。

次の例では、ポインターを宣言して、直ちにその内容をプリントしています。

var p *int32
fmt.Println(" p = ", p) // p =  <nil>

nil を fmt.Println で表示すると <nil> と表示されます。

Go のポインターのデリファレンス

Go のポインター変数に * を付けると、そのポインター変数が保持しているアドレスに書き込まれている値を意味します。

この場合の *間接演算子 (indirection operator) といいます。そして、間接演算子を使ってポインターが保持するアドレスを参照することをデリファレンス (dereferencing) といいます。

次の例では6行目で変数 x のアドレスを、ポインター変数 p に代入。そして、 8行目でポインター p が指し示す内容 (つまり変数 x の値) を書き換えています。9行目では x の値が書き換えられていることが確認できます。

var x int32 = 10
fmt.Println(" x = ", x)  //  x =  10
fmt.Println("&x = ", &x) // &x =  0xc00001407c

var p *int32
p = &x
fmt.Println(" p = ", p) //   p =  0xc00001407c
*p = 20
fmt.Println(" x = ", x) //   x =  20

Go のポインターの算術演算は許可されていない

Go のポインターのシンタックスは C 言語に由来していますので、 C 言語に慣れている方には馴染みのあるものだと思います。

特定のバッファのポインターを取得したら、データサイズ分ポインターを足したりひいたりしてバッファの内容を参照する、ということはよくやる操作ですね。

しかし、Go では C/C++ 言語と違ってそうしたポインターの算術演算は許可されていません。

Go のポインターのまとめ

以上、Go のポインターについて説明しました。

いろんな名前が出てきて、ウッとひるんだ方もいるかもしれませんので、最後に簡単に内容をまとめておきます。

  • 普通の変数のアドレスは & で取り出せる (& は「アドレス演算子」)
  • ポインター変数はアドレスを保持する
  • ポインター変数が保持するアドレスに書き込まれた値は * を付けて参照できる (* は「間接演算子」。間接演算子でアドレスの参照する値を参照することは「デリファレンス」)
  • Go のポインターは算術演算はない

このポイントを抑えておけば、基本的なコードを書くのに十分です。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Go 言語入門