今回のお話
今回のお話はGoのポインタです。
Goは基本的に守備範囲外なのですが、面白かったので自分なりにまとめます。
目次
- ポインタとは
- ポインタのメリット
- ポインタの取得方法
- ポインタ型変数
- ポインタから変数を取得する方法
- 実際に使ってみよう
ポインタとは
ポインタとは、メモリ上のどこに変数の値が保存されているのかを表したものになります。
いわば、変数の住所のようなものですね。
ちなみに、ポインタというのはGoの世界での呼び名であり、他の言語ではアドレスと呼ぶこともあるようです。
ポインタは基本的に16進数で表されます。
0xc123456789
ポインタのメリット
ポインタの一番のメリットは、「スコープ外の変数を利用できる」という点です。
スコープとは、変数が利用できる範囲のことですね。
一般に、変数はその関数の外側に持ち出すことができません。
以下がその例です。
func main(){
a := 10
addOne(a) // 出力:11
println(a) // 出力:10
}
func addOne(a int){
a++
println(a)
}
func main()以下を見ると、addOneで呼び出した結果は11ですが、その後に変数aを出力すると10に戻っていますね。
func mainで定義した変数aとaddOneに渡したaは同一ではなく、あくまでコピーを渡しただけなのでこのようなことが起こります。
もし、addOneを用いてaの値を更新したければ、以下のようにする必要があります。
func main(){
a := 10
a = addOne(a)
println(a) // 出力:11
}
func addOne(a int)int{
a++
return a
}
ですが、今回のお題であるポインタを用いると、addOneメソッドの中で直接aの更新ができるようになります。
ポインタの取得方法
では、肝心のポインタはどのように取得すればよいのでしょうか?
答えは簡単で、変数名の先頭に&をつければポインタを取得できます。
name := "kenn"
println(&name) //結果:0xc123456789
ポインタ型変数
ところで、Goには変数や引数を定義する際には型を明示するというルールがありました。
ポインタが代入された変数をポインタ型変数と呼ぶのですが、ポインタ型変数の型はどのように記述するのでしょうか。
実は、ポインタ型変数に関しては専用の型は用意されておらず、参照している変数の型の先頭に*をつけたものがポインタ型変数の型になります。
num := 10
name := "taro"
var numPtr = *int
var namePtr = *string
numPtr = &num
namePtr = &name
これで、ポインタを変数に入れることができるようになりました。
では、今度はポインタから変数にアクセスしてみましょう。
ポインタから変数を取得する方法
変数名の先頭に&をつけるとポインタが取得できました。
逆に、ポインタ型変数の先頭に*をつけると変数の値が取得できます。
num := 10
var numPtr int*
numPtr = &num
println(*numPtr) //結果:10
では、これを用いて実際にスコープ外の変数にアクセスしてみましょう。
実際に使ってみよう
最初にお見せしたmain, addOneを以下のように書き換えます。
package main
func main(){
a := 10
addOne(&a)
println(a)// 結果:11
}
func addOne(aPtr *int){
*aPtr++
}
これまでのようにaddOneの結果をわざわざaに再代入することなくaの値が更新されています。
終わりに
以上がポインタのお話でした。
これまでに学習した言語にはない概念だったので最初は戸惑いましたが、結果的に変数のスコープに関する理解が深まったように思います。