ポインタを扱う (*)

Go 言語には、C/C++ と同様にポインタが存在します。構文もかなり似ていますが、簡潔に記述できるような工夫がされています。

Go 言語のポインタの基本

Go 言語では、変数の型のプレフィックスとしてアスタリスク (*) を付けると、ポインタ型の変数になります。 ポインタ変数は、その型の値が格納されているメモリアドレスを保持します。 ポインタ変数のゼロ値(初期値)は nil です。

var p *int             // p is a pointer to int
fmt.Printf("%v\n", p)  // <nil>

既存の変数のアドレスは & プレフィックスをつけて取得できます。 次の例では、int 型変数 i のアドレスを p に格納しています。

i := 100
p := &i

fmt.Printf("%v (%T)\n", i, i)  // 100 (int)
fmt.Printf("%v (%T)\n", p, p)  // 0xc00018a000 (*int)

逆にポインタが指し示す値にアクセスするには、ポインタ変数の前に * を付けて参照します。 下記の例では、ポインタ経由で参照先の値を書き換えています。

var i int = 100
var p *int = &i

fmt.Println(i)   //=> 100
fmt.Println(*p)  //=> 100
*p = 200         // ポインタ経由で値を書き換える
fmt.Println(i)   //=> 200
fmt.Println(*p)  //=> 200

このあたりのポインタの文法は、C/C++ 言語とほとんど同じです。 ただし、Go 言語ではポインタ演算(アドレスの足し算など)を行うことはできません。 そういったアクセスを禁止することで、不正なメモリアドレスへアクセスしてしまう危険性を排除しています。

関数内から呼び出し元の変数の値を書き換える

Go 言語の関数のパラメーターは、通常は値渡し(値のコピーが渡される)となるため、下記のような関数を実行しても呼び出し側の数値を変更することはできません。

func add100(n int) {
	n += 100  // 呼び出し側の値は変更されない
}

関数のパラメーターをポインタ型にすると、呼び出し元の変数が格納されているメモリアドレスを受け取ることができます。 そのメモリアドレスに格納されている値を、関数内から直接書き換えることができるようになるため、結果的に呼び出し側でパラメーターとして渡した変数の値を書き換えることができます。 下記のコードでは、int のポインタを受け取り、呼び出し側の int 変数の値を変更しています。

func add100(n *int) {
	*n += 100  // 呼び出し側の値を変更できる
}

func main() {
	n := 50
	add100(&n)
	println(n)  //=> 150
}