はじめに
書籍「プログラミング言語Go」に以下の説明があります。
ポインタは変数のアドレスを持っていますので、関数へポインタ引数を渡すことで、関数が間接的に渡された変数を更新することができるようになります。
サンプルコードも記載されていましたが、直感的に理解できなかったのでサンプルコードを動かしながら理解を深めた過程が本投稿になります。
本題
以下2つのコードの違いを確認してみました。
①関数に変数 v
のアドレスを渡している
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
func main() {
v := 1
fmt.Println(incr(&v))
fmt.Println(incr(&v))
}
func incr(p *int) int {
*p++
return *p
}
https://go.dev/play/p/CwocnClX7x1
実行結果
2
3
②関数に変数 v
のを渡している
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
func main() {
v := 1
fmt.Println(incr(v))
fmt.Println(incr(v))
}
func incr(p int) int {
p++
return p
}
https://go.dev/play/p/yDCGl9VKF9Y
実行結果
2
2
②のコードは理解できました。引数で受け取ったローカル変数をインクリメントする関数なので、常に return 2
となることはわかりました。
では次に①を確認していきます。かなりスマートじゃない方法ですが、printデバッグで処理経過を追ってみました。
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
func main() {
v := 1
fmt.Println(&v)
fmt.Println(incr(&v, "1回目"))
fmt.Println(v)
fmt.Println(&v)
fmt.Println(incr(&v, "2回目"))
}
func incr(p *int, str string) int {
fmt.Println(str)
fmt.Println(p)
fmt.Println(*p)
*p++
fmt.Println(*p)
fmt.Println(p)
fmt.Println("関数抜ける")
return *p
}
https://go.dev/play/p/1qtDacPAU1y
0xc0000b8000
1回目
0xc0000b8000
1
2
0xc0000b8000
関数抜ける
2
2
0xc0000b8000
2回目
0xc0000b8000
2
3
0xc0000b8000
関数抜ける
3
常に同じポインタを渡していることがわかりました。冒頭で引用した文章の意味は、ポインタを渡すことで関数 incr
が変数 v
を間接的に更新できることを言っているんですね。やっと引数にポインタを指定する意図を少しだけ理解できた気がします。
参考元
□プログラミング言語Go
https://www.maruzen-publishing.co.jp/item/?book_no=295039