細說Go語言陣列
目錄
陣列是具有相同 唯一型別 的一組已編號且長度固定的資料項序列(這是一種同構的資料結構);這種型別可以是任意的原始型別例如整形、字串或者自定義型別(結構體、介面)。陣列長度必須是一個常量表達式,並且必須是一個非負整數,陣列下標是從0開始的,並且陣列的長度是固定不可變的,陣列中元素的地址是連續的。陣列長度也是陣列型別的一部分,所以[5]int和[10]int是屬於不同型別的。
在其它程式語言中,陣列一般都是引用型別,而在Go語言中,陣列屬於值型別。
注意:如果我們想讓陣列元素型別為任意型別的話可以使用空介面作為型別。當使用值時我們必須先做一個型別判斷。
宣告陣列
Go語言陣列宣告需要指定元素型別及元素個數,語法格式如下:
//變數名[大小]型別 var variable_name [len]variable_type
示例:
定義一個長度為5的陣列arr
var arr [5]int
以上我們聲明瞭一個數組 arr
,但是我們還沒有對他進行初始化,但是宣告的變數都會有預設值,int型別的變數預設值是0,所以arr陣列中的每個元素的值都是0。
Go語言中的陣列是一種 值型別 ,所以可以通過 new() 來建立: var arr1 = new([5]int)
。那麼這種方式和 var arr2 [5]int
的區別是什麼呢?arr1 的型別是 *[5]int
,而 arr2的型別是 [5]int
。
陣列在記憶體的結構
假如我們宣告一個長度為5的陣列 var arr [5]int
,我們來看看這個陣列在記憶體中的結構是怎麼樣的。
上圖就是一個Go語言陣列在記憶體的結構,在這個arr陣列中,由於我們宣告的是int32型別,所以陣列中的每個元素在記憶體中佔用的位元組數是32/8=4位元組,通過arr陣列每個元素16進位制地址我們可以發現,陣列中後一個元素的地址比前一個元素的地址大4個位元組,這個4位元組正好是int32型別元素佔用的記憶體大小,由此我們也能得出結論:陣列中元素的記憶體地址是連續的;當宣告陣列時所有的元素都會被自動初始化為陣列型別的預設值(int型別的預設值是0,所以arr中每個元素的預設值都是0);陣列的下標識從0開始的。陣列arr的地址就是陣列第一個元素(下標為0的元素)的地址,通過下圖列印arr的地址和arr[0]的地址就可以證明
初始化陣列
剛剛宣告的陣列已經被預設的元素型別零值初始化了,如果我們再次進行初始化怎麼做呢,可以採用如下辦法:
var arr [5]int arr = [5]int{1, 2, 3, 4, 5}
Go語言提供了宣告加初始化的 :=
操作符,以讓我們在建立陣列的時候直接初始化。
arr := [5]int{1, 2, 3, 4, 5}
這種簡短變數宣告的方式不僅適用於陣列,還適用於任何資料型別,這也是Go語言中常用的方式。
我們也可以在定義陣列是不明確指定長度,讓編譯器自動推匯出長度,可以使用 ...
來替代具體的長度
arr := [...]int{1, 2, 3, 4, 5}
如果我們只想給陣列的部分元素指定值,其他元素採用預設值我們可以採用下面的辦法
arr := [5]int{0:3, 4:6}
這種方式表示我們只給陣列下標為0的元素賦值為3,下標為4的元素賦值為6,其它元素的值依然是該型別的預設值(int的預設值是0)。
使用陣列
陣列元素的訪問非常簡單,通過索引(下標)即可訪問陣列的元素。
arr := [5]int{1, 2, 3, 4, 5} fmt.Println(arr[0], arr[1], arr[2], arr[3], arr[4])
修改陣列的某個元素也很簡單:
arr := [5]int{1, 2, 3, 4, 5} fmt.Println(arr[0]) arr[0] = 66 fmt.Println(arr[0])
如果我們要迴圈列印陣列中的所有值,一個傳統的就是常用的for迴圈:
for i := 0; i < len(arr); i++ { fmt.Println(arr[i]) }
不過大部分時候,我們都是使用for rang迴圈:
for i, v := range arr { fmt.Println("索引是:", i, "值是:", v) }
同樣型別的陣列是可以相互賦值的,不同型別的不行,會編譯錯誤。那麼什麼是同樣型別的陣列呢?Go語言規定,必須是長度一樣,並且每個元素的型別也一樣的陣列,才是同樣型別的陣列。
arr := [5]int{1, 2, 3, 4, 5} var arr2 [5]int = arr //可以 var arr3 [3]int = arr //不可以
指標陣列和陣列本身差不多,只不過元素型別是指標。
arr := [5]*int{1: new(int), 3:new(int)}
這樣就建立了一個指標陣列,並且為索引1和3都建立了記憶體空間,其他索引是指標的零值 nil
,這時候我們要修改指標變數的值也很簡單,如下即可:
array := [5]*int{1: new(int), 3:new(int)} *array[1] = 1
以上需要注意的是,只可以給索引1和3賦值,因為只有它們分配了記憶體,才可以賦值,如果我們給索引0賦值,執行的時候,會提示無效記憶體或者是一個nil指標引用。
panic: runtime error: invalid memory address or nil pointer dereference
要解決這個問題,我們要先給索引0分配記憶體,然後再進行賦值修改。
arr := [5]*int{1: new(int), 3:new(int)} arr[0] =new(int) *arr[0] = 2 fmt.Println(*arr[0])
函式間傳遞陣列
在Go中,陣列也是值型別,所以在函式間傳遞變數時,麼就會整個複製,並傳遞給函式,如果陣列非常大,比如長度100多萬,那麼這對記憶體是一個很大的開銷。
func main() { array := [5]int{1: 2, 3:4} //[0 2 0 4 0] modify(array) fmt.Println(array) //[0 2 0 4 0] } func modify(a [5]int){ a[1] = 3 }
通過上面的例子,可以看到,陣列是複製的,原來的陣列沒有修改。我們這裡是5個長度的陣列還好,如果有幾百萬怎麼辦,有一種辦法是傳遞陣列的指標,這樣,複製的大小隻是一個數組型別的指標大小。
func main() { array := [5]int{1: 2, 3:4} //[0 2 0 4 0] modify(&array) fmt.Println(array) //[0 333 0 4 0] } func modify(a *[5]int){ a[1] = 333 }
這裡注意,陣列的指標和指標陣列是兩個概念,陣列的指標是 *[5]int
,指標陣列是 [5]*int
,注意 *
的位置。
陣列使用注意事項
-
陣列是多個相同型別資料的組合,一個數組一旦宣告/定義了,其長度是固定的,不能動態變化。
-
陣列中的元素可以是任何資料型別,包括值型別和引用型別,但是不能混用。
-
陣列建立後,如果沒有賦值,有預設值
數值型別陣列:預設值為0
字串型別陣列:預設值為""
bool陣列: 預設值為false
指標陣列: 預設值nil
-
使用陣列的步驟:1.宣告陣列並開闢空間2.給陣列各個元素賦值3.使用陣列
-
陣列的下標從0開始
-
陣列下標必須在指定範圍內使用,否則報panic:陣列越界
-
Go的陣列屬於 值型別 ,在預設情況下是值傳遞,因此會進行值拷貝。陣列間不會相互影響
-
如果想在其它函式中,去修改原來的陣列,可以使用引用傳遞(指標方式)
-
長度是陣列型別的一部分,在傳遞函式引數時,需要考慮陣列的長度