我是陣列--就要學習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來了」,獲取最新文章!
