スライス(2)

スライスは make 関数を使っても生成できる。

変数名 := make([]型, 大きさ, 容量)

「大きさ」と「容量」があるけど、「大きさ」は要素の数(ここではゼロ値に初期化される)、「容量」はメモリ上に確保される領域のサイズのこと、と言ったらいいかな。「容量」は省略できる。
「大きさ」、「容量」はそれぞれ len 関数、cap 関数で得ることができる。

package main

import "fmt"

func main() {
    a := make([]int, 5, 10)

    fmt.Println(a)
    fmt.Println(len(a))
    fmt.Println(cap(a))
}
^o^ > go run slice4.go
[0 0 0 0 0]
5
10

スライスは、append 関数を使って末尾に要素を追加できる。

package main

import "fmt"

func main() {
    a := make([]int, 0)
    fmt.Println(a)

    for i := 0; i < 5; i++ {
        a = append(a, i)
        fmt.Println(a)
    }
}
^o^ > go run slice5.go
[]
[0]
[0 1]
[0 1 2]
[0 1 2 3]
[0 1 2 3 4]

容量を超えて要素を追加しようとすると、スライスはよりを大きい容量の新しいメモリ領域を確保する。これは自動的に行われる。ちょっと試してみよう。

package main

import "fmt"

func main() {
    a := make([]int, 0)

    fmt.Printf("len=%d cap=%d %p %v\n", len(a), cap(a), a, a)

    for i := 1; i < 18; i++ {
        a = append(a, i)
        fmt.Printf("len=%d cap=%d %p %v\n", len(a), cap(a), a, a)
    }
}
^o^ > go run slice6.go
len=0 cap=0 0x526b88 []
len=1 cap=1 0xc0420381d8 [1]
len=2 cap=2 0xc042038220 [1 2]
len=3 cap=4 0xc04203e500 [1 2 3]
len=4 cap=4 0xc04203e500 [1 2 3 4]
len=5 cap=8 0xc042040140 [1 2 3 4 5]
len=6 cap=8 0xc042040140 [1 2 3 4 5 6]
len=7 cap=8 0xc042040140 [1 2 3 4 5 6 7]
len=8 cap=8 0xc042040140 [1 2 3 4 5 6 7 8]
len=9 cap=16 0xc042052180 [1 2 3 4 5 6 7 8 9]
len=10 cap=16 0xc042052180 [1 2 3 4 5 6 7 8 9 10]
len=11 cap=16 0xc042052180 [1 2 3 4 5 6 7 8 9 10 11]
len=12 cap=16 0xc042052180 [1 2 3 4 5 6 7 8 9 10 11 12]
len=13 cap=16 0xc042052180 [1 2 3 4 5 6 7 8 9 10 11 12 13]
len=14 cap=16 0xc042052180 [1 2 3 4 5 6 7 8 9 10 11 12 13 14]
len=15 cap=16 0xc042052180 [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
len=16 cap=16 0xc042052180 [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16]
len=17 cap=32 0xc042032100 [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17]

%p は変数のポインタを表す書式指定子。上の例を見ると、大きさ(len)が容量(cap)を超えるタイミングで容量も大きくなり、同時にポインタの値も変化していく様子が見て取れる。