iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 9
2
Software Development

Go繁不及備載系列 第 9

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

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

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

陣列

【Array 陣列】

陣列的長度在宣告之後無法改變

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

切片

【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)

宣告變數blen:5cap: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都是一樣的值,底層陣列只要一份資料就好,夠省空間吧!
Pointer

操作 Slice

【Append】

通常會用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一切乍看之下都非常的美滿,但事情總是發生的令人措手不及...來繼續往下。

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的地方,原因在於底層的陣列被後來的_蓋掉了。


上一篇
# Day08 Go先歇一會-小教基本型別(bool, int, string, rune)
下一篇
# Day10 繼續Go-基本組合型別2(map, struct)
系列文
Go繁不及備載35
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
hungohung
iT邦新手 5 級 ‧ 2020-12-11 14:33:40

你好,

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

我以為在"足夠容量"那個case, 是發生災情的case. 因為b指到的值被改掉了. 在"超小容量"的case, b值反而維持在[1,2,3]

Anyway, 給跟我一樣頭腦炸掉無法理解的朋友. 可以參考一下這篇對於Slice內部機制的講解

https://medium.com/@riteeksrivastava/how-slices-internally-work-in-golang-a47fcb5d42ce

Cheers

我要留言

立即登入留言