我是陣列--就要學習Go語言
前言
Go 語言給使用者提供了三種資料結構用於管理集合資料:陣列、切片(Slice
)和對映(Map
)。這三種資料結構是語言核心的一部分,在標準庫裡被廣泛使用。學會這些資料結構,編寫go程式會變得快速、有趣且十分靈活。掌握陣列是理解切片和對映的基礎,我們就從陣列開始學習。
什麼是陣列
Go語言中,陣列是一個長度固定的資料型別,用於儲存一段相同資料型別的元素,這些元素在記憶體中是連續儲存的。陣列儲存的型別可以是內建型別,如整型、字串等,也可以是自定義的資料結構。強調陣列固定,有別於切片,它是可以增長和收縮的動態序列。陣列的每個元素可以通過索引下標來訪問,索引下標的範圍是從[0 , len(array)-1]
宣告與初始化
陣列宣告有兩個要點:
- 指定陣列儲存的資料的型別;
- 元素個數,即陣列的長度;
var array0 [5]int // 宣告一個包含5個元素的整型陣列,但我們並未初始化
fmt.Println(array0) //輸出:[0 0 0 0 0]
複製程式碼
前面我們已經講過,Go 語言中宣告變數時,總會使用對應型別的零值來對變數進行初始化。陣列也不例外。 當陣列初始化時,陣列內每個元素都初始化為對應型別的零值。從輸出結果可以看到,整型數組裡的每個元素都初始化為 0,也就是整型的零值。
var array0 [5]int
array0 = [5]int{1,2,3,4,5} //手動初始化
fmt.Println(array0) //輸出:[1 2 3 4 5]
複製程式碼
最基本的宣告並初始化:
// 宣告並初始化
var array0 = [5]int{1,2,3,4,5}
fmt.Println(array0)
複製程式碼
使用Go提供的:=
操作符:
array0 := [5]int{1,2,3,4,5}
複製程式碼
Go提供了一種機制,免去了我們指定陣列長度的煩惱,使用...
,根據初始化時陣列元素的數量來確定該陣列的長度。
array := [...]int{1,2,3,4,5}
fmt.Println(len(array)) // 內建的len()函式返回陣列中元素的個數。
複製程式碼
假如我只想給索引為1、3的元素指定初始化的值怎麼辦?還是有辦法的:
array := [...]int{0,2,0,4,0}
複製程式碼
更簡便的方法:
array := [5]int{1:2,3:4}
複製程式碼
學會使用陣列
上面提到過得,因為陣列的記憶體分佈是連續的,所以在陣列訪問任一的效率是很高,這也是陣列的優勢。可以使用[]
運算子訪問陣列的當個元素。
array := [5]int{1,2,3,4,5}
fmt.Println(array[3]) //訪問單個元素
array[3] = 30 //修改當個元素的值
fmt.Println(array[3])
複製程式碼
使用for
、for range
迴圈遍歷陣列:
array := [5]int{1,2,3,4,5}
// for
for i:=0;i<len(array);i++ {
fmt.Printf("索引%d的值: %d\n",i,array[i])
}
// for range
for i,v := range array{
fmt.Printf("索引%d的值: %d\n",i,v)
}
複製程式碼
輸出的結果是一樣的:
索引0的值: 1
索引1的值: 2
索引2的值: 3
索引3的值: 4
索引4的值: 5
複製程式碼
陣列變數的型別包括陣列長度和每個元素的型別。Go語言規定只有這兩部分都相同的陣列,才是型別相同的陣列,才能互相賦值,不然會編譯出錯。
var array1 [5]int
array2 := [5]int{1,2,3,4,5}
array1 = array2
fmt.Println(array1) // 輸出:[1 2 3 4 5]
var array3 [4]int = array2
// 編譯出錯:cannot use array2 (type [5]int) as type [4]int in assignment
複製程式碼
陣列指標和指標陣列
我們可以宣告一個指標變數,指向一個數組:
arr := [6]int{5:9}
// 陣列指標
var ptr *[6]int = &arr
// 簡寫
ptr := &arr
複製程式碼
需要注意的是,指標變數ptr
的型別是*[6]int
,也就是說它只能指向包含6個元素的整型陣列,否則編譯報錯。 指標陣列和陣列差不多,只不過元素型別是指標:
// 指標陣列
x,y := 1,2
var arrPtr = [5]*int{1:&x,3:&y} // 沒有手動初始化的元素,已經自動初始化指標型別對應的零值 nil
fmt.Println(*arrPtr[1]) // 輸出:1
*arrPtr[1] = 10
fmt.Println(x,*arrPtr[1]) // 輸出:10 10
複製程式碼
*arrPtr[1] = 10
,同時也修改了變數x
的值,因為x
和arrPtr[1]
指向同一記憶體地址。 提一點,相同型別的指標陣列也可以相互賦值。 總結一句話:注意*與
誰結合,如p *[5]int
,*
與陣列結合說明是陣列指標;如p [5]*int
,*
與int
結合,說明這個陣列都是int
型別的指標,是指標陣列。
函式間傳遞陣列
函式之間傳遞變數時, 總是以值的方式傳遞的。如果變數是一個數組,意味著整個陣列,不管有多大,都會完整賦值一份,並傳遞給函式。複製出來的陣列只是原陣列的一份副本,在函式中修改傳遞進來陣列是不會改變原陣列的值得。
// 傳遞陣列的副本
func modify(a [5]int) {
a[1] = 1
fmt.Println(a)
}
func main(){
arr := [5]int{4:9}
fmt.Println(arr)
modify(arr)
fmt.Println(arr)
}
複製程式碼
輸出:
[0 0 0 0 9]
[0 1 0 0 9]
[0 0 0 0 9]
複製程式碼
原陣列元素的值沒有被修改。
大家可以想一個問題,如果一個數組的資料量很大,如果還採用值傳遞的話,這無疑是一個開銷很大的操作,對記憶體和效能都是不友好的。還好,我們還有一個更好的辦法:傳遞指向陣列的指標,這樣只需要複製一個數組型別的指標大小就可以。
// 傳遞陣列的指標
func modifyPtr(a *[5]int){
a[1] = 2
fmt.Println(*a)
}
func main(){
arr := [5]int{4:9}
fmt.Println(arr)
modifyPtr(&arr)
fmt.Println(arr)
}
複製程式碼
輸出:
[0 0 0 0 9]
[0 2 0 0 9]
[0 2 0 0 9]
複製程式碼
有沒有發現!原陣列已經改變了,因為現在傳遞的是陣列指標, 所以如果改變指標指向的值,原陣列在記憶體的值也會被修改。這種操作雖然更有效地利用記憶體(免去了大量的記憶體複製)、效能也更好,但如果沒有處理好指標,也會帶來不必要的問題,所以,使用的時候需要謹慎小心。
下一節,我們來講講切片!
關注公眾號「Golang來了」,獲取最新文章!