---
# System prepended metadata

title: 'Day09 Go繼續開車-基本組合型別(array, slice)'

---

# Day09 Go繼續開車-基本組合型別(array, slice)

組合（複合）型別，顧名思義是建構在基礎型別之上，把上一章節所教的型別組合在一起。

## 陣列
#### 【Array 陣列】
陣列的長度在宣告之後無法改變  

https://play.golang.org/p/PkeZt8WRY5B  

```go
var a [5]int
a[0] = 10
a[1] = 100
a[2] = 1000
fmt.Println(a)
```

值也可以一開始就給，可部分宣告值，沒值的預設為`零值`  
```go
b := [5]int{1, 2, 3} //單行宣告
fmt.Println(b)

c := [5]int{
	10,
	20,
	30,
	55,	//使用多行宣告的話，最後一個元素要逗號
}
fmt.Println(c[1:3])

d := [...]int{4, 6, 8}	//用...省略符號，讓go判斷長度
fmt.Println(d)
/* result:
[10 100 1000 0 0]
[1 2 3 0 0]
[20 30]
[4 6 8]
*/
```

#### 【微坑注意】
```go
e:= []int{1,2}
```
這種`[]`內沒任何東西的宣告是屬於`Slice`，  
而不是省略長度的`Array`  



## 切片
#### 【Slice 切片】
切片是啥？肉切片？組織切片？  
這名詞真的是學Go時第一次看到，還能不潮嗎？  

`陣列(array)`使用上非常方便，  
但每次都得預先宣告一個長度、撥一段記憶體空間給陣列使用。  
為了更加靈活，Google說這邊有一個物件叫 `Slice`，無腦用就對了。  

官方文檔的介紹：
> An array has a fixed size.  
> A slice, on the other hand, is a dynamically-sized,  
> flexible view into the elements of an array.  

宣告時中括號[]裡為空
把`Slice`拆開來看，這玩意包含了三樣東西：  
1. 長度 `len` ，舉個例子，就是現在的長度  ~~5 cm~~  
2. 容量 `cap` ，最大能容納的長度 ~~30cm~~  
3. 指針 `ptr` ，透過指針 我們可以與別人共用同一個地方  

用文字敘述 好像有點越描越黑的感覺，我們還是趕緊來看範例...  

宣告`Slice`的方法之一：

> **Variable := make([]Type, Len, Cap)**
>> b := make([]int, 5, 10)  

宣告`變數b`為`len:5`、`cap:10`~~的男人~~ 的整數切片。  

以下是幾種宣告`Slice`的方法：
https://play.golang.org/p/p9isnfQh1wW  

```go
a := make([]int, 10) //設定 len:10。現在長度10了，容量雖然沒給，但最大容納長度當然不可能小於10吧，所以就是10了
fmt.Println(a, len(a), cap(a), len(a) == 0, a == nil)

b := make([]int, 5, 10) //設定 len:5、cap:10
fmt.Println(b, len(b), cap(b), len(b) == 0, b == nil)

var c = []int{} //初始化slice
fmt.Println(c, len(c), cap(c), len(c) == 0, c == nil)

var d []int //尚未實體化，此時等於nil
fmt.Println(d, len(d), cap(d), len(d) == 0, d == nil)

e := []string{"youtube.com", "facebook.com"} //直接賦值
fmt.Println(e, len(e), cap(e), len(e) == 0, e == nil)

/* result:
[0 0 0 0 0 0 0 0 0 0] 10 10 false false
[0 0 0 0 0] 5 10 false false
[] 0 0 true false
[] 0 0 true true
[youtube.com facebook.com]
*/

```
初始化沒給值的話都是`零值`。  

Slice並不是真正存值，而是透過指針指到更下面的地方，某個陣列  
這也是為什麼說可以跟別人共用的原因。  
如果SliceA跟SliceB都是一樣的值，底層陣列只要一份資料就好，夠省空間吧！  
![Pointer](https://i.imgur.com/WskKaXf.png)

## 操作 Slice
#### 【Append】
通常會用`append`這個方法來操作`Slice`  
```go
x := []int{1, 2, 3}
x = append(x, 4, 5, 6)
fmt.Println(x)

/* result:
[1 2 3 4 5 6]
*/
```

一開始學到`容量cap`時，我也覺得這功能有點雞肋。  
明明`容量cap`不夠時、`長度len`超過`容量cap`的時候，  
再來個`append`就好，`容量cap`會自動擴充啊！  
不就等於我設定了一個我可以隨時打破的規定，那我要這功能幹嘛？？？  
`Slice`一切乍看之下都非常的美滿，但事情總是發生的令人措手不及...來繼續往下。    

## Slice【坑很多】
一騎來看看花生什麼杯具了  
```go
a := make([]int, 0, 10) //給足夠容量
b := append(a, 1, 2, 3)
_ = append(a, 99, 88, 77)
fmt.Println(b)

//--------

aa := make([]int, 0, 2) //給超小容量
bb := append(aa, 1, 2, 3)
_ = append(aa, 99, 88, 77)
fmt.Println(bb)

/* result:
[99 88 77]
[1 2 3]
*/
```

拿你口雷？  
在`足夠容量`中沒發生災情，但是在`超小容量`中卻因為後來的 `_` 做了 `append` ，  
導致我的`aa`中的值也被改掉了？  

沒綽，這就是可以看到`pointer`的地方，原因在於底層的陣列被後來的`_`蓋掉了。  
