はじめに
こんにちはRIN1208です。今回はITRCアドベントカレンダーの二日目の記事です。
今回書くのは本日12月2日に六本木ヒルズで行われたStep up Go で初めて知って今後もハマりそうなことを勉強しましたので忘れないように書いていきたいと思います。
実行環境はplay.golangを使用します(なぜかmacだと問題を再現できませんでした)
#今回の問題
package main
func main() {
a := []int{1, 2, 3}
b := append(a, 4, 5)
a[0] = 10
c := append(b, 6, 7)
b[1] = 20
println(c[0] + c[1])
}
こちらのコードです。
実行すると....
21
は??????
なんで???3じゃないの?と思いました。普通ポインタとスライスの中身を見てみると
package main
import "fmt"
func main() {
a := []int{1, 2, 3}
b := append(a, 4, 5)
a[0] = 10
c := append(b, 6, 7)
b[1] = 20
println("%p", a)
println("%p", b)
println("%p", c)
fmt.Println(a, b, c)
println(c[0] + c[1])
}
出力結果
%p [3/3]0x40e020
%p [5/8]0x456000
%p [7/8]0x456000
[10 2 3] [1 20 3 4 5] [1 20 3 4 5 6 7]
21
bとcが同じポインタ??????
goのsliceはこちらのような構造になっているらしいです。
また同じポインタを参照しているので反映されてしまっているみたいです。
ちなみにcapを更新?するとポインタも変わるみたいです
package main
import "fmt"
func main() {
a := []int{1, 2, 3}
b := append(a, 4, 5)
a[0] = 10
c := append(b, 6, 7)
b[1] = 20
d := append(c, 6, 7)
c[1] = 30
println("%p", a)
println("%p", b)
println("%p", c)
println("%p", d)
fmt.Println(cap(a), cap(b), cap(c), cap(d))
fmt.Println(a, b, c, d)
println(c[0] + c[1])
}
----------------------------
%p [3/3]0x40e020
%p [5/8]0x456000
%p [7/8]0x456000
%p [9/16]0x430080
3 8 8 16
[10 2 3] [1 30 3 4 5] [1 30 3 4 5 6 7] [1 20 3 4 5 6 7 6 7]
31
#結論
goの配列は途中保存するときは上記の書き方ではなくcopy関数を使用し別の変数に格納した方がいいみたいです
#終わりに
今回、自分の未熟さゆえに曖昧な部分が多いのに加え適切な言い方ではない箇所があるためとてもいい記事ではないと思います。ですが今回初めてこんな挙動をすると知ったので知らない人に知っていただければと思い書きました。また本記事について詳しく知っている方はコメントにて教えて頂けるととても助かります。