組合(複合)型別,顧名思義是建構在基礎型別之上,把上一章節所教的型別組合在一起。
陣列的長度在宣告之後無法改變
https://play.golang.org/p/PkeZt8WRY5B
var a [5]int
a[0] = 10
a[1] = 100
a[2] = 1000
fmt.Println(a)
值也可以一開始就給,可部分宣告值,沒值的預設為零值
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]
*/
e:= []int{1,2}
這種[]
內沒任何東西的宣告是屬於Slice
,
而不是省略長度的Array
切片是啥?肉切片?組織切片?
這名詞真的是學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
拆開來看,這玩意包含了三樣東西:
len
,舉個例子,就是現在的長度 cap
,最大能容納的長度 ptr
,透過指針 我們可以與別人共用同一個地方用文字敘述 好像有點越描越黑的感覺,我們還是趕緊來看範例...
宣告Slice
的方法之一:
Variable := make([]Type, Len, Cap)
b := make([]int, 5, 10)
宣告變數b
為len:5
、cap:10
的男人 的整數切片。
以下是幾種宣告Slice
的方法:
https://play.golang.org/p/p9isnfQh1wW
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都是一樣的值,底層陣列只要一份資料就好,夠省空間吧!
通常會用append
這個方法來操作Slice
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
一切乍看之下都非常的美滿,但事情總是發生的令人措手不及...來繼續往下。
一騎來看看花生什麼杯具了
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
的地方,原因在於底層的陣列被後來的_
蓋掉了。
你好,
拿你口雷?
在足夠容量中沒發生災情,但是在超小容量中卻因為後來的 _ 做了 append ,
導致我的aa中的值也被改掉了?
我以為在"足夠容量"那個case, 是發生災情的case. 因為b指到的值被改掉了. 在"超小容量"的case, b值反而維持在[1,2,3]
Anyway, 給跟我一樣頭腦炸掉無法理解的朋友. 可以參考一下這篇對於Slice內部機制的講解
https://medium.com/@riteeksrivastava/how-slices-internally-work-in-golang-a47fcb5d42ce
Cheers