認識 Go 語言中的陣列

highhand發表於2021-09-09

作者:
程式設計拯救世界(ID: CodeWarrior_):專注於程式設計基礎與服務端研發。


陣列是具有相同唯一型別的一組已編號長度固定的資料項序列。

宣告方式

在 Go 語言中,陣列的宣告方式為 var identifier [len]type

宣告時沒有指定陣列的初始化值,因此所有的元素都會被自動初始化為預設值 0

// 宣告一個陣列
var a1 [5]int

Go 語言中的陣列是值型別,因此還可以用 new 來建立:

var a2 = new([5]int)

new 返回型別的指標,因此 a1a2 的區別在於:a1 的型別為 [5]inta2 的型別為 *[5]int

初始化方法

我們透過在 {} 中填寫初始化值來初始化陣列。

指明陣列長度

指明陣列的長度 len,然後在 {} 中填寫初始化值,這些值會按下標從小到大的順序分配。

初始化值的個數不允許超過長度 len。當初始化值的個數小於 len 時,未被初始化的位置等於預設值 0。

// 陣列長度為 5,初始化了前兩個數,未初始化的位是 0
b := [5]int{1, 2} 
for index, val := range b {
	fmt.Printf("下標 = %d, 值 = %dn", index, val)
}

/* Output:
下標 = 0, 值 = 1
下標 = 1, 值 = 2
下標 = 2, 值 = 0
下標 = 3, 值 = 0
下標 = 4, 值 = 0
*/

也可以使用 {index1: a, index2: b} 的方式初始化陣列,指明陣列的索引和對應的下標值,未指明的下標所在位置的值等於預設值 0:

// 透過陣列索引初始化
// d[0] = 1, d[2] = 3,其他位置等於 0
d := [5]int{0: 1, 2: 3} 
for index, val := range d {
	fmt.Printf("下標 = %d, 值 = %dn", index, val)
}

/* Output:
下標 = 0, 值 = 1
下標 = 1, 值 = 0
下標 = 2, 值 = 3
下標 = 3, 值 = 0
下標 = 4, 值 = 0
*/

暗示陣列長度

初始化時你也可以不直接指明陣列的長度,而是使用 [...] 代替。和指明陣列長度時相同,此時也可以使用順序填寫和指定索引兩種方式來初始化陣列。

當使用 {a, b, c} 方式傳遞初始化值時,Go 語言將透過初始化元素的個數來確定陣列的長度

// 透過傳遞初始化值確定陣列長度
// 傳遞了 5 個元素,陣列長度為 5
c := [...]int{1, 2, 3, 4, 5}  
for index, val := range c {
	fmt.Printf("下標 = %d, 值 = %dn", index, val)
}
/* Output:
下標 = 0, 值 = 1
下標 = 1, 值 = 2
下標 = 2, 值 = 3
下標 = 3, 值 = 4
下標 = 4, 值 = 5
*/

若透過指明陣列的索引和對應的值來初始化陣列,此時陣列的長度就等於 最大索引數 + 1

// 最大索引是 9,所以陣列的長度為 10
e := [...]int{9: 10} 
for index, val := range e {
	fmt.Printf("下標 = %d, 值 = %dn", index, val)
}

/* Output:
下標 = 0, 值 = 0
下標 = 1, 值 = 0
下標 = 2, 值 = 0
下標 = 3, 值 = 0
下標 = 4, 值 = 0
下標 = 5, 值 = 0
下標 = 6, 值 = 0
下標 = 7, 值 = 0
下標 = 8, 值 = 0
下標 = 9, 值 = 10
*/

陣列的遍歷

在 Go 語言中使用 for ... range 遍歷陣列:

// i 是陣列索引
for i, _ := range arr1 {
    // do something
}

陣列是值型別

和 C/C++ 不同,Go 語言的陣列是值型別的。這樣一來,賦值和傳參都會複製整個陣列,而不是指標

我們初始化一個陣列 a,並把它賦值給陣列 b,然後列印兩者的值與指標:

a := [5]int{1, 2, 3, 4, 5}
b := a
fmt.Printf("陣列 a - 值:%v,指標:%pn", a, &a)
fmt.Printf("陣列 b - 值:%v,指標:%pn", b, &b)
/* Output:
陣列 a - 值:[1 2 3 4 5],指標:0xc00001e0f0
陣列 b - 值:[1 2 3 4 5],指標:0xc00001e120
*/

可以看到,兩者的值是相同的,但是記憶體地址卻不同,說明在賦值的過程中複製了整個陣列

我們再來看一下傳參的例子。

定義一個函式 transmitA,把剛才我們初始化的陣列 a 傳入:

func main() {
	a := [5]int{1, 2, 3, 4, 5}
	fmt.Printf("陣列 a - 值:%v,指標:%pn", a, &a)
	
	// 把陣列 a 傳入函式
	transmitA(a)
}

func transmitA(a [5]int) {
	fmt.Printf("傳入函式的陣列 a - 值:%v,指標:%pn", a, &a)
}

/* Output:
陣列 a - 值:[1 2 3 4 5],指標:0xc00001e0f0
傳入函式的陣列 a - 值:[1 2 3 4 5],指標:0xc00001e150
*/

從輸出可以看出,兩者的值依然相同,記憶體地址卻是不同的。這說明在傳參時陣列也被複制了

陣列指標與指標陣列

陣列指標與指標陣列聽起來似乎有點拗口,那麼來展開說明一下:

  • 陣列指標:(指向)陣列(的)指標
  • 指標陣列:(裝滿了)指標(的)陣列

也就是說,陣列指標是個指標,它指向一個陣列;而指標陣列是個陣列,它裡面裝滿了指標

陣列指標

宣告一個陣列 a,然後將它的地址賦值給 arrayPointer。這樣一來,arrayPointer 就是一個指向陣列 a 的指標,即陣列指標,它的型別為 *[5]int

a := [5]int{1, 2, 3, 4, 5}
// 把陣列 a 的地址賦值給 arrayPointer
// arrayPointer 是指向陣列的指標,型別為 *[5]int
arrayPointer := &a
fmt.Println(arrayPointer)

/* Output:
&[1 2 3 4 5]
*/

指標陣列

初始化陣列 pointerArray,傳入的初始化值為整型 mn 的記憶體地址(&m&n),那麼 pointerArray 就是一個裝著 int 型別指標的陣列,即指標陣列,它的型別為 [2]*int

m := 1
n := 2
// 初始化 pointerArray,傳入 m 與 n 的地址
// pointerArray 包含了整型地址,是一個裝著指標的陣列
pointerArray := [2]*int{&m, &n}
fmt.Println(pointerArray)

/* Output:
[0xc0000aa000 0xc0000aa008]
*/

總結

  • 陣列的長度是固定的,初始化時需要「明示」或「暗示」陣列的長度
  • 陣列的長度是陣列型別的組成部分[2]int[100]int 是不同型別的陣列
  • 使用 for ... range 遍歷陣列
  • 在 Go 語言中,陣列是值型別,賦值和傳遞引數都會發生陣列的複製
  • 陣列指標是一個指標,它指向了一個陣列
  • 指標陣列是一個陣列,它裡面裝著指標

參考資料

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/810/viewspace-2824278/,如需轉載,請註明出處,否則將追究法律責任。

相關文章