- Go語言高級編程(第2版)
- 柴樹杉 曹春暉
- 1991字
- 2025-08-07 17:56:12
1.3.1 數組
數組是一個由固定長度的特定類型元素組成的序列,一個數組可以由零個或多個元素組成。數組的長度是數組類型的一部分,所以不同長度或不同類型的元素組成的數組都是不同類型的數組。因此,Go語言中很少直接使用數組(因不同長度的數組屬于不同的數組類型而無法直接賦值)。與數組對應的類型是切片,切片是可以動態增長和收縮的序列,切片的功能也更加靈活,但是要理解切片的工作原理還是要先理解數?組。
我們先看看數組有哪些定義方式:
var a [3]int // 定義長度為3的int型數組,元素全部為0 var b = [...]int{1, 2, 3} // 定義長度為3的int型數組,元素為1, 2, 3 var c = [...]int{2: 3, 1: 2} // 定義長度為3的int型數組,元素為0, 2, 3 var d = [...]int{1, 2, 4: 5, 6} // 定義長度為6的int型數組,元素為1, 2, 0, 0, 5, 6
第一種方式是定義一個數組變量的最基本的方式,數組的長度明確指定,數組中的每個元素都以零值初始?化。
第二種方式是在定義數組的時候順序指定全部元素的初始值,數組的長度根據初始化元素的數目自動計?算。
第三種方式是以索引的方式來初始化數組的元素,因此元素的初始值出現順序比較隨意。這種初始化方式和map[int]Type
類型的初始化語法類似。數組的長度以出現的最大的索引為準,沒有明確初始化的元素依然用零值初始?化。
第四種方式是混合了第二種和第三種的初始化方式,前面兩個元素采用順序初始化,第三個和第四個元素采用零值初始化,第五個元素通過索引初始化,最后一個元素跟在前面的第五個元素之后采用順序初始?化。
數組的內存結構比較簡單。例如,圖1-6給出的是數組[4]int{2,3,5,7}
的內存結?構。

圖1-6 數組[4]int{2,3,5,7}
的內存結構
Go語言中數組是值語義。一個數組變量即表示整個數組,它并不是隱式地指向第一個元素的指針(C語言的數組變量是指針),而是一個完整的值。當一個數組變量被賦值或者被傳遞的時候,實際上會復制整個數組。如果數組較大的話,數組的賦值也會有較大的開銷。為了避免復制數組帶來的開銷,可以傳遞一個指向數組的指針,但是數組指針并不是數?組。
var a = [...]int{1, 2, 3} // a是一個數組 var b = &a // b是指向數組的指針 fmt.Println(a[0], a[1]) // 打印數組的前兩個元素 fmt.Println(b[0], b[1]) // 通過數組指針訪問數組元素的寫法和直接訪問數組類似 for i, v := range b { // 通過數組指針遍歷數組的元素 fmt.Println(i, v) }
其中b
是指向數組a
的指針,但是通過b
訪問數組中元素的寫法和直接訪問a
是
類似的。還可以通過for range
來遍歷數組指針指向的數組元素。其實數組指針類型除類型和數組不同之外,通過數組指針操作數組的方式和通過數組本身的操作類似,而且數組指針賦值時只會復制一個指針。但是數組指針類型依然不夠靈活,因為數組的長度是數組類型的一部分,指向不同長度數組的數組指針類型也是完全不同?的。
可以將數組看作一個特殊的結構體,結構的字段名對應數組的索引,同時結構體成員的數目是固定的。內置函數len()
可以用于計算數組的長度,cap()
函數可以用于計算數組的容量。不過對數組類型來說,len()
和cap()
函數返回的結果始終是一樣的,都是對應數組類型的長?度。
我們可以用for
循環來遍歷數組。下面常見的幾種方式都可以用來遍歷數組:
for i := range a { fmt.Printf("a[%d]: %d\n", i, a[i]) } for i, v := range b { fmt.Printf("b[%d]: %d\n", i, v) } for i := 0; i < len(c); i++ { fmt.Printf("c[%d]: %d\n", i, c[i]) }
for range
循環的性能可能會更好一些,因為這種循環可以保證不會出現數組越界的情形,在每次迭代對數組元素訪問時可以省去對下標越界的判?斷。
使用for range
循環
遍歷還可以忽略迭代時的下標:
var times [5][0]int for range times { fmt.Println("hello") }
其中,times
對應一個[5][0]int
類型的數組,雖然第一維數組有長度,但是數組的元素[0]int
大小是0
,因此整個數組占用的內存大小依然是0
。不用付出額外的內存代價,我們就通過for range
循環實現了times
次快速迭代(Go 1.22已經支持基于一個整數的for range
用法)。
數組不僅可以定義數值數組,還可以定義字符串數組、結構體數組、函數數組、接口數組、通道數組等:
// 字符串數組 var s1 = [2]string{"hello", "world"} var s2 = [...]string{"你好", "世界"} var s3 = [...]string{1: "世界", 0: "你好", } // 結構體數組 var line1 [2]image.Point var line2 = [...]image.Point{image.Point{X: 0, Y: 0}, image.Point{X: 1, Y: 1}} var line3 = [...]image.Point{{0, 0}, {1, 1}} // 圖像解碼器數組 var decoder1 [2]func(io.Reader) (image.Image, error) var decoder2 = [...]func(io.Reader) (image.Image, error){ png.Decode, jpeg.Decode, } // 接口數組 var unknown1 [2]interface{} var unknown2 = [...]interface{}{123, "你好"} // 通道數組 var chanList = [2]chan int{}
我們還可以定義一個空的數組:
var d [0]int // 定義一個長度為0的數組 var e = [0]int{} // 定義一個長度為0的數組 var f = [...]int{} // 定義一個長度為0的數組
長度為0的數組(空數組)在內存中并不占用空間。空數組雖然很少直接使用,但是可以用于強調某種特有類型的操作時避免分配額外的內存空間,如用于通道的同步操作:
c1 := make(chan [0]int) go func() { fmt.Println("c1") c1 <- [0]int{} }() <-c1
在這里,我們并不關心通道中傳輸數據的真實類型,其中通道接收和發送操作只是用于消息的同步。對于這種場景,我們用空數組作為通道類型可以減少通道元素賦值時的開銷。當然,一般更傾向于用無類型的匿名結構體代替空數組:
c2 := make(chan struct{}) go func() { fmt.Println("c2") c2 <- struct{}{} // struct{}部分是類型,{}表示對應的結構體值 }() <-c2
我們可以用fmt
.Printf()
函數提供的%T
或%#v
謂詞語法來打印數組的類型和詳細信息:
fmt.Printf("b: %T\n", b) // b: [3]int fmt.Printf("b: %#v\n", b) // b: [3]int{1, 2, 3}
在Go語言中,數組類型是切片和字符串等結構的基礎。以上對于數組的很多操作都可以直接用于字符串或切片?中。
- 極簡算法史:從數學到機器的故事
- Visual C++數字圖像模式識別技術詳解
- Java開發入行真功夫
- Learning Laravel 4 Application Development
- bbPress Complete
- Creating Stunning Dashboards with QlikView
- Java網絡編程實戰
- ElasticSearch Cookbook(Second Edition)
- Visual Basic程序設計(第三版)
- Mockito Essentials
- PHP項目開發全程實錄(第4版)
- 原型設計:打造成功產品的實用方法及實踐
- Python滲透測試編程技術:方法與實踐(第2版)
- Spring Boot從入門到實戰
- MySQL數據庫教程(視頻指導版)