naofunky
@naofunky

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Goでのmake関数を用いてスライスを作成して容量を確認した時の挙動がどういうこと!

Q&A

解決したいこと

make関数でスライスを作成して、appendで要素を追加した時のcapメソッドで容量を確認すると要素数と異なり、挙動の仕方がどのようになっているのかお伺いしたいです。

発生している問題・エラー

[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15] 15 16

該当するソースコード

main.go
	s4 := make([]int, 0, 3)
	s4 = append(s4, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
	fmt.Println(s4, len(s4), cap(s4))

自分で試したこと

変数s4を定義してmake関数でスライスを初期化しました。その後に、appendメソッドで要素を数値で1~15まで追加しました。そして、最後に要素数と容量をコンソールに出力しました。自身の予測だと要素数と同じ数、容量になるのかなと思ったのですが、要素数+1になってしまいます。その要因が調べても理解できないので、この部分でいい参考記事等ありましたら教示いただけたら幸いです。イラストや図解のある、参考だと助かります。

0

1Answer

ざっと見た感じだとこのへんが図が多用されていてわかりやすそうです。

私自身は Go について知らないということはお断りしておきます。 低レイヤ寄りの言語におけるメモリ管理の常識で考えると普通の動作として理解できるので、以下はそういう一般論です。

メモリ上に「連続した」領域を確保しようとするとき、そう自由に長さを変えられるわけではありません。 長くしたいときに隣に他のオブジェクトがあったりするともっと大きな空いている連続する領域を探してそこに移し替えるという操作が必要です。 親切なランタイムサポートがある言語やライブラリだとそういう操作を裏でやってくれるわけです。

当然ですが、配列を他の場所に移し替えるという操作はそう頻繁にやりたいことではありません。 たくさんのコピーをするのは実行コストが高いことだからです。 なのである程度に余裕をとって領域を確保しておき、足りなくなったときにまた再配置をするという形で管理されます。 なのでキャパシティはその余裕を含む分です。

どれくらいの余裕を用意するのが効率的なのかは諸説ありますが現在の Go の場合は対象の大きさが小さい内 (1024 まで) は倍々で伸ばしていく仕組みのようです。 なのでこの場合の容量は 2 のべき乗である 16 が得られたのでしょう。

3Like

Comments

  1. @naofunky

    Questioner

    上記ご回答ありがとうございます。

    ご丁寧に解説ありがとうございます。
    参考記事もわかりやすく、とても勉強になりました。

Your answer might help someone💌