數(shù)組是具有相同 唯一類型 的一組已編號且長度固定的數(shù)據(jù)項序列(這是一種同構(gòu)的數(shù)據(jù)結(jié)構(gòu));這種類型可以是任意的原始類型例如整型、字符串或者自定義類型。數(shù)組長度必須是一個常量表達式,并且必須是一個非負整數(shù)。數(shù)組長度也是數(shù)組類型的一部分,所以[5]int和[10]int是屬于不同類型的。數(shù)組的編譯時值初始化是按照數(shù)組順序完成的(如下)。
注意事項 如果我們想讓數(shù)組元素類型為任意類型的話可以使用空接口作為類型(參考 第 11 章)。當使用值時我們必須先做一個類型判斷(參考 第 11 章)。
數(shù)組元素可以通過 索引(位置)來讀?。ɑ蛘咝薷模?,索引從 0 開始,第一個元素索引為 0,第二個索引為 1,以此類推。(數(shù)組以 0 開始在所有類 C 語言中是相似的)。元素的數(shù)目,也稱為長度或者數(shù)組大小必須是固定的并且在聲明該數(shù)組時就給出(編譯時需要知道數(shù)組長度以便分配內(nèi)存);數(shù)組長度最大為 2Gb。
聲明的格式是:
var identifier [len]type
例如:
var arr1 [5]int
在內(nèi)存中的結(jié)構(gòu)是:http://wiki.jikexueyuan.com/project/the-way-to-go/images/7.1_fig7.1.png?raw=true" alt="" />
每個元素是一個整型值,當聲明數(shù)組時所有的元素都會被自動初始化為默認值 0。
arr1 的長度是 5,索引范圍從 0 到 len(arr1)-1
。
第一個元素是 arr1[0]
,第三個元素是 arr1[2]
;總體來說索引 i 代表的元素是 arr1[i]
,最后一個元素是 arr1[len(arr1)-1]
。
對索引項為 i 的數(shù)組元素賦值可以這么操作:arr[i] = value
,所以數(shù)組是 可變的。
只有有效的索引可以被使用,當使用等于或者大于 len(arr1)
的索引時:如果編譯器可以檢測到,會給出索引超限的提示信息;如果檢測不到的話編譯會通過而運行時會 panic:(參考 第 13 章)
runtime error: index out of range
由于索引的存在,遍歷數(shù)組的方法自然就是使用 for 結(jié)構(gòu):
示例 7.1 for_arrays.go
package main
import "fmt"
func main() {
var arr1 [5]int
for i:=0; i < len(arr1); i++ {
arr1[i] = i * 2
}
for i:=0; i < len(arr1); i++ {
fmt.Printf("Array at index %d is %d\n", i, arr1[i])
}
}
輸出結(jié)果:
Array at index 0 is 0
Array at index 1 is 2
Array at index 2 is 4
Array at index 3 is 6
Array at index 4 is 8
for 循環(huán)中的條件非常重要:i < len(arr1)
,如果寫成 i <= len(arr1)
的話會產(chǎn)生越界錯誤。
IDIOM:
for i:=0; i < len(arr1); i++{
arr1[i] = ...
}
也可以使用 for-range 的生成方式:
IDIOM:
for i,_:= range arr1 {
...
}
在這里i也是數(shù)組的索引。當然這兩種 for 結(jié)構(gòu)對于切片(slices)(參考 第 7 章)來說也同樣適用。
問題 7.1 下面代碼段的輸出是什么?
a := [...]string{"a", "b", "c", "d"}
for i := range a {
fmt.Println("Array item", i, "is", a[i])
}
Go 語言中的數(shù)組是一種 值類型(不像 C/C++ 中是指向首元素的指針),所以可以通過 new()
來創(chuàng)建: var arr1 = new([5]int)
。
那么這種方式和 var arr2 [5]int
的區(qū)別是什么呢?arr1 的類型是 *[5]int
,而 arr2的類型是 [5]int
。
這樣的結(jié)果就是當把一個數(shù)組賦值給另一個時,需要在做一次數(shù)組內(nèi)存的拷貝操作。例如:
arr2 := *arr1
arr2[2] = 100
這樣兩個數(shù)組就有了不同的值,在賦值后修改 arr2 不會對 arr1 生效。
所以在函數(shù)中數(shù)組作為參數(shù)傳入時,如 func1(arr2)
,會產(chǎn)生一次數(shù)組拷貝,func1 方法不會修改原始的數(shù)組 arr2。
如果你想修改原數(shù)組,那么 arr2 必須通過&操作符以引用方式傳過來,例如 func1(&arr2),下面是一個例子
示例 7.2 pointer_array.go:
package main
import "fmt"
func f(a [3]int) { fmt.Println(a) }
func fp(a *[3]int) { fmt.Println(a) }
func main() {
var ar [3]int
f(ar) // passes a copy of ar
fp(&ar) // passes a pointer to ar
}
輸出結(jié)果:
[0 0 0]
&[0 0 0]
另一種方法就是生成數(shù)組切片并將其傳遞給函數(shù)(詳見第 7.1.4 節(jié))。
練習
練習7.1:array_value.go: 證明當數(shù)組賦值時,發(fā)生了數(shù)組內(nèi)存拷貝。
練習7.2:for_array.go: 寫一個循環(huán)并用下標給數(shù)組賦值(從 0 到 15)并且將數(shù)組打印在屏幕上。
練習7.3:fibonacci_array.go: 在第 6.6 節(jié)我們看到了一個遞歸計算 Fibonacci 數(shù)值的方法。但是通過數(shù)組我們可以更快的計算出 Fibonacci 數(shù)。完成該方法并打印出前 50 個 Fibonacci 數(shù)字。
如果數(shù)組值已經(jīng)提前知道了,那么可以通過 數(shù)組常量 的方法來初始化數(shù)組,而不用依次使用 []=
方法(所有的組成元素都有相同的常量語法)。
示例 7.3 array_literals.go
package main
import "fmt"
func main() {
// var arrAge = [5]int{18, 20, 15, 22, 16}
// var arrLazy = [...]int{5, 6, 7, 8, 22}
// var arrLazy = []int{5, 6, 7, 8, 22}
var arrKeyValue = [5]string{3: "Chris", 4: "Ron"}
// var arrKeyValue = []string{3: "Chris", 4: "Ron"}
for i:=0; i < len(arrKeyValue); i++ {
fmt.Printf("Person at %d is %s\n", i, arrKeyValue[i])
}
}
第一種變化:
var arrAge = [5]int{18, 20, 15, 22, 16}
注意 [5]int
可以從左邊起開始忽略:[10]int {1, 2, 3}
:這是一個有 10 個元素的數(shù)組,除了前三個元素外其他元素都為 0。
第二種變化:
var arrLazy = [...]int{5, 6, 7, 8, 22}
...
可同樣可以忽略,從技術(shù)上說它們其實變化成了切片。
第三種變化:key: value syntax
var arrKeyValue = [5]string{3: "Chris", 4: "Ron"}
只有索引 3 和 4 被賦予實際的值,其他元素都被設(shè)置為空的字符串,所以輸出結(jié)果為:
Person at 0 is
Person at 1 is
Person at 2 is
Person at 3 is Chris
Person at 4 is Ron
在這里數(shù)組長度同樣可以寫成 ...
或者直接忽略。
你可以取任意數(shù)組常量的地址來作為指向新實例的指針。
示例 7.4 pointer_array2.go
package main
import "fmt"
func fp(a *[3]int) { fmt.Println(a) }
func main() {
for i := 0; i < 3; i++ {
fp(&[3]int{i, i * i, i * i * i})
}
}
輸出結(jié)果:
&[0 0 0]
&[1 1 1]
&[2 4 8]
幾何點(或者數(shù)學向量)是一個使用數(shù)組的經(jīng)典例子。為了簡化代碼通常使用一個別名:
type Vector3D [3]float32
var vec Vector3D
數(shù)組通常是一維的,但是可以用來組裝成多維數(shù)組,例如:[3][5]int
,[2][2][2]float64
。
內(nèi)部數(shù)組總是長度相同的。Go 語言的多維數(shù)組是矩形式的(唯一的例外是切片的數(shù)組,參見第 7.2.5 節(jié))。
示例 7.5 multidim_array.go
package main
const (
WIDTH = 1920
HEIGHT = 1080
)
type pixel int
var screen [WIDTH][HEIGHT]pixel
func main() {
for y := 0; y < HEIGHT; y++ {
for x := 0; x < WIDTH; x++ {
screen[x][y] = 0
}
}
}
把一個大數(shù)組傳遞給函數(shù)會消耗很多內(nèi)存。有兩種方法可以避免這種現(xiàn)象:
接下來的例子闡明了第一種方法:
示例 7.6 array_sum.go
package main
import "fmt"
func main() {
array := [3]float64{7.0, 8.5, 9.1}
x := Sum(&array) // Note the explicit address-of operator
// to pass a pointer to the array
fmt.Printf("The sum of the array is: %f", x)
}
func Sum(a *[3]float64) (sum float64) {
for _, v := range a { // derefencing *a to get back to the array is not necessary!
sum += v
}
return
}
輸出結(jié)果:
The sum of the array is: 24.600000
但這在 Go 中并不常用,通常使用切片(參考 第 7.2 節(jié))。