Go言語 — slice と array とポインタ

# まとめ

- スライス は 配列 のラッパーのようなもの。
- スライス への操作をおこなうと、内部ではいい感じで 配列を操作してくれる。 スライス はその 配列 を参照する。
- スライス という名前には 「配列の断片を使う」というニュアンスがあるように感じられる。
  - この動作を便宜的に「要素数を気にしないタイプの配列」として扱えるイメージ。
- Gopher の間では「スライスだけで良いじゃん」という説もあるようだ。


# 検証

## 1. 配列をスライスする

配列から一部の要素だけをスライスしてみる。
(スライスっていう名前通りの使い方)

```go
array := [3]string {"Alice", "Bob", "Carol"}
slice := array[:2]
```

一部の要素だけをスライスしたので、得られる要素数は、スライスのほうが一個少ない。
配列にはCarorlがいるが、スライスにはいない。

```
fmt.Println(array) // [Alice Bob Carol]
fmt.Println(slice) // [Alice Bob]
```


## 2. スライスを変更する

Alice を Dave に変えてみる。

```go
slice[0] = "Dave"
```

配列とスライスの両方で、Alice が Dave に変わった。
スライスは実体ではなく、配列を参照しているからだ。

```go
fmt.Println(array) // [Dave Bob Carol]
fmt.Println(slice) // [Dave Bob]
```

「スライスの基底となる配列」のポインタを見ることも出来る。

```go
underlying := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
fmt.Println(underlying) // 例: &{842350559520 2 3}
```

## 3. 配列を変更する

今度はスライスではなく、配列側の値を変更してみる。

```go
array[1] = "Eric"
```

配列とスライスの両方で、Bob が Eric に変わった。

```
fmt.Println(array) // [Dave Eric Carol]
fmt.Println(slice) // [Dave Eric]
```

引き続きスライスは同じポインタで配列を参照している。

```go
fmt.Println(underlying) // 例: &{842350559520 2 3}
```

## 4. スライスに要素を追加する

2個の要素が得られているスライスに、3個目の要素を追加してみる。

```go
slice = append(slice, "Frank")
```

配列の3個目の要素が変わった。 Carol は Frank　になった。

```go
fmt.Println(array) // [Dave Bob Frank]
fmt.Println(slice) // [Dave Bob Frank]
```

スライスの cap の値が 2 から 3 に増えているが、ポインタは変わっていない。

```go
fmt.Println(underlying) // 例: &{842350559520 3 3}
```

## 5. 配列の要素数を越える

Go での配列は、要素数の決まった型だ。
つまりこの例でいうと、配列は3個の string しか持っていない。 ( `[3]string` という型 )

スライスの末尾にさらに要素を追加してみる。 

```go
slice = append(slice, "Greg") 
```

- 配列の要素数は 3個
- スライスはその配列を参照している

と考えると、スライスに4個目の要素は追加できないはずだが？

結果を見てみると、今度は配列とスライスで得られる値が変わっている。
スライスにだけ Greg が追加されている。

```go
fmt.Println(array) // [Dave Eric Frank]
fmt.Println(slice) // [Dave Eric Frank Greg]
```

試しに、スライスの1個目の要素を変更してみる。

```go
slice[0] = "Henry"
```

そうすると、また配列は変更されていない。
スライスでだけ Dave が Henry に変わっている。

```go
fmt.Println(array) // [Dave Eric Frank]
fmt.Println(slice) // [Henry Eric Frank Greg]
```

ポインタの状態を見てみると、ポインタの参照先が変わっている。

```
fmt.Println(underlying) // 例: &{842350895200 4 6}
```

配列とスライスの「リンク」が切れた状態になったようだ。

goのスライスは、今回のように【配列の要素数を越えるスライス】を作った場合、内部的に新しい配列を作り、今度はそいつを参照するようになるようだ。


# pointer / len / cap

ちなみに、スライスは `ポインタ / 長さ(len) / キャパシティ(cap)` という構造になっているが、

最初のポインタが

- どの配列を参照するか
- 配列の何番目から何番目までを参照するか

を覚えていてくれている。

![image](https://qiita-image-store.s3.amazonaws.com/0/89618/6860fca6-0293-aed9-b48b-58579cee4b35.png)

( [Go Slices: usage and internals - The Go Blog](https://blog.golang.org/go-slices-usage-and-internals) より )

- len はスライスで得られる要素数
- cap は基底となる配列の要素数

っぽい。

>The length of a slice is the number of elements it contains.
>The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.

( [A Tour of Go](https://tour.golang.org/moretypes/11) より )

ただコードの最後の例で cap が 3 から 6 に増えているのは謎なので、今後調べたい。

# コード全体

```go

package main

import(
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	array := [3]string {"Alice", "Bob", "Carol"} // 配列を作る
	slice := array[:2] // 配列から二個の要素をスライスする
	fmt.Println(array) // [Alice Bob Carol]
	fmt.Println(slice) // [Alice Bob]

	slice[0] = "Dave"  // スライスの先頭を変更する
	fmt.Println(array) // [Dave Bob Carol] // 配列の値が変更される
	fmt.Println(slice) // [Dave Bob] // スライスは配列を参照している

	underlying := (*reflect.SliceHeader)(unsafe.Pointer(&slice)) // スライスのポインタを見てみる
	fmt.Println(underlying) // 例: &{842350559520 2 3}

	array[1] = "Eric"  // スライスではなく、配列側の中身を変更してみる
	fmt.Println(array) // [Dave Eric Carol] // Bob が Eric に変わった
	fmt.Println(slice) // [Dave Eric] // スライスは配列を参照しているので、スライスで得られる値も変わる

	fmt.Println(underlying) // 例: &{842350559520 2 3} // ポインタが同じ

	slice = append(slice, "Frank") // スライスの末尾に要素を追加する // 得られる要素数は三個になる
	fmt.Println(array) // [Dave Bob Frank] // 配列の三個目の要素が変更された
	fmt.Println(slice) // [Dave Bob Frank] // スライスは配列の要素全てを参照するようになった 

	fmt.Println(underlying) // 例: &{842350559520 3 3} 

	slice = append(slice, "Greg") // スライスの末尾にさらに要素を追加する // 参照先の配列の要素数を越えて4個になりそうだが？
	fmt.Println(array) // [Dave Eric Frank] // 今度は配列の状態は変わっていない
	fmt.Println(slice) // [Dave Eric Frank Greg] // スライスで得られる要素数は四個になった // スライスは配列を参照していたはずでは？

        fmt.Println(underlying) // 例: &{842350895200 4 6} // ポインタの参照先が変わっている

	slice[0] = "Henry" // スライスの最初の要素を変更する
	fmt.Println(array) // [Dave Eric Frank] // 今度も配列の状態は変わっていない
	fmt.Println(slice) // [Henry Eric Frank Greg] // スライスで得られる値は期待通りに変わっている

	fmt.Println(underlying) // 例: &{842350895200 4 6} // ポインタの参照先が変わっている
}
```


# 環境

- go version go1.8 darwin/amd64

# 感想

- 低級言語の仕組みを知れて良かった。
- 当たり前だが高級言語でも内部的には、こういったことがおこなわれているんだろうなと思った。
- 配列の扱いひとつでも、メモリ使用量とかを気にする気持ちが少し分かった気がする。

# 参考

- [Go Slices: usage and internals - The Go Blog](https://blog.golang.org/go-slices-usage-and-internals)
- [The curious case of Golang array and slices](https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335#.8bdmo4zdh)
- [How to get the underlying array of a slice in Go? - Stack Overflow](http://stackoverflow.com/questions/36706843/how-to-get-the-underlying-array-of-a-slice-in-go)









<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->