はじめに
今日から3日連続で、Go初心者向けのやさしい記事を公開します。
初日はポインタです。
プログミング初心者がつまずきやすい、Goのポインタまわりをなるべく簡潔に分かりやすくまとめました。
C言語よりは学習ハードルが低いので安心してください。
アドレスとポインタ
アドレス
アドレスとは、16進数で表現されたメモリの番地(場所)のことです。
&
(アドレス演算子)を変数の前に付けることで任意の型からポインタ型を定義できます。(Cも同様)
ポインタ型が格納された変数のことをポインタ変数と呼びます。下でいえば、 b
がポインタ変数になります。
func main() {
var a int
fmt.Println(&a)
b := &a
fmt.Println(b)
}
0xc000014090
0xc000014090
ポインタ変数から値を参照するためには *
を変数の前に付けます。これをデリファレンスと呼びます。
func main() {
a := 1
b := &a
fmt.Println(*b)
}
1
ポインタ
ポインタとは、「型情報+アドレス」のことです。
func main() {
a := 1
b := &a
fmt.Printf("型情報: %T /アドレス/: %p", b, b)
}
型情報: *int /アドレス: 0xc00001e060
*
を型の前に付けることでポインタ型を定義できます。
初期値はnilです。
func main() {
var a *int // 変数aはint型のポインタ型
fmt.Println(a)
}
<nil>
また、 *
を複数つけて「ポインタのポインタの・・・」みたいことができます。
とはいえ、普段のソフトウェア開発で使用するケースはほとんどないでしょう。
func main() {
var a *****int
fmt.Println(a)
}
<nil>
ポインタがあると何が嬉しいか?
ここまで、淡々と説明してきましたが、これで何ができるようになるのでしょうか?
それは、値型の変数をポインタの値渡しできることです。
値渡し
package main
import "fmt"
func sampleFunc(a int) {
a += 1
fmt.Println("②:", a)
}
func main() {
a := 10
fmt.Println("①:", a)
sampleFunc(a)
fmt.Println("③:", a)
}
①: 10
②: 11
③: 10
注目は③です。渡したのはコピーなのでもとの値は変わりません。
これは、sampleFunc(a)
で値渡しをしているからです。
ポインタの値渡し
package main
import "fmt"
func sampleFunc(a *int) {
*a += 1
fmt.Println("②:", *a)
}
func main() {
a := 10
fmt.Println("①:", a)
sampleFunc(&a)
fmt.Println("③:", a)
}
①: 10
②: 11
③: 11
今度の③は変わります。
sampleFunc(&a)
で渡したのが変数aのアドレスなので、渡した方も渡された方も同じものを見ていることになります。
また、参照型の変数であるスライスやマップの場合は、ポインタ型を渡さなくてももとからポインタの値渡しになっています。
func sampleFunc(a []int) {
a[0] = 2
fmt.Println("②:", a)
}
func main() {
a := make([]int, 1)
a[0] = 1
fmt.Println("①:", a)
sampleFunc(a)
fmt.Println("③:", a)
}
①: [1]
②: [2]
③: [2]
package main
import "fmt"
func sampleFunc(a map[int]string) {
a[0] = "tokyo"
fmt.Println("②:", a)
}
func main() {
a := make(map[int]string)
a[0] = "osaka"
fmt.Println("①:", a)
sampleFunc(a)
fmt.Println("③:", a)
}
①: map[0:osaka]
②: map[0:tokyo]
③: map[0:tokyo]
Goでは参照型の変数はスライスとマップとチャネルの3つだけです。(残りは値型かポインタ型です。)
まとめると、ポインタの値渡しをするためには「値型の変数であればポインタを渡す」、「参照型の変数であればそのまま渡す」ということになります。
ちなみにですが、Goには「参照渡し」は存在せず、すべて「値渡し」となります。
参考
少し古いですが、Goの文法を書籍で勉強するならこれ一択な気がします。
さいごに
明日は、【Go初心者向けのやさしい記事】Goのインターフェースを10分で学ぼうという記事です。こちらもお楽しみに!