Go 語(yǔ)言 數(shù)組

2023-03-14 16:52 更新

原文鏈接:https://gopl-zh.github.io/ch4/ch4-01.html


4.1. 數(shù)組

數(shù)組是一個(gè)由固定長(zhǎng)度的特定類型元素組成的序列,一個(gè)數(shù)組可以由零個(gè)或多個(gè)元素組成。因?yàn)閿?shù)組的長(zhǎng)度是固定的,因此在Go語(yǔ)言中很少直接使用數(shù)組。和數(shù)組對(duì)應(yīng)的類型是Slice(切片),它是可以增長(zhǎng)和收縮的動(dòng)態(tài)序列,slice功能也更靈活,但是要理解slice工作原理的話需要先理解數(shù)組。

數(shù)組的每個(gè)元素可以通過索引下標(biāo)來訪問,索引下標(biāo)的范圍是從0開始到數(shù)組長(zhǎng)度減1的位置。內(nèi)置的len函數(shù)將返回?cái)?shù)組中元素的個(gè)數(shù)。

var a [3]int             // array of 3 integers
fmt.Println(a[0])        // print the first element
fmt.Println(a[len(a)-1]) // print the last element, a[2]

// Print the indices and elements.
for i, v := range a {
    fmt.Printf("%d %d\n", i, v)
}

// Print the elements only.
for _, v := range a {
    fmt.Printf("%d\n", v)
}

默認(rèn)情況下,數(shù)組的每個(gè)元素都被初始化為元素類型對(duì)應(yīng)的零值,對(duì)于數(shù)字類型來說就是0。我們也可以使用數(shù)組字面值語(yǔ)法用一組值來初始化數(shù)組:

var q [3]int = [3]int{1, 2, 3}
var r [3]int = [3]int{1, 2}
fmt.Println(r[2]) // "0"

在數(shù)組字面值中,如果在數(shù)組的長(zhǎng)度位置出現(xiàn)的是“...”省略號(hào),則表示數(shù)組的長(zhǎng)度是根據(jù)初始化值的個(gè)數(shù)來計(jì)算。因此,上面q數(shù)組的定義可以簡(jiǎn)化為

q := [...]int{1, 2, 3}
fmt.Printf("%T\n", q) // "[3]int"

數(shù)組的長(zhǎng)度是數(shù)組類型的一個(gè)組成部分,因此[3]int和[4]int是兩種不同的數(shù)組類型。數(shù)組的長(zhǎng)度必須是常量表達(dá)式,因?yàn)閿?shù)組的長(zhǎng)度需要在編譯階段確定。

q := [3]int{1, 2, 3}
q = [4]int{1, 2, 3, 4} // compile error: cannot assign [4]int to [3]int

我們將會(huì)發(fā)現(xiàn),數(shù)組、slice、map和結(jié)構(gòu)體字面值的寫法都很相似。上面的形式是直接提供順序初始化值序列,但是也可以指定一個(gè)索引和對(duì)應(yīng)值列表的方式初始化,就像下面這樣:

type Currency int

const (
    USD Currency = iota // 美元
    EUR                 // 歐元
    GBP                 // 英鎊
    RMB                 // 人民幣
)

symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"}

fmt.Println(RMB, symbol[RMB]) // "3 ¥"

在這種形式的數(shù)組字面值形式中,初始化索引的順序是無(wú)關(guān)緊要的,而且沒用到的索引可以省略,和前面提到的規(guī)則一樣,未指定初始值的元素將用零值初始化。例如,

r := [...]int{99: -1}

定義了一個(gè)含有100個(gè)元素的數(shù)組r,最后一個(gè)元素被初始化為-1,其它元素都是用0初始化。

如果一個(gè)數(shù)組的元素類型是可以相互比較的,那么數(shù)組類型也是可以相互比較的,這時(shí)候我們可以直接通過==比較運(yùn)算符來比較兩個(gè)數(shù)組,只有當(dāng)兩個(gè)數(shù)組的所有元素都是相等的時(shí)候數(shù)組才是相等的。不相等比較運(yùn)算符!=遵循同樣的規(guī)則。

a := [2]int{1, 2}
b := [...]int{1, 2}
c := [2]int{1, 3}
fmt.Println(a == b, a == c, b == c) // "true false false"
d := [3]int{1, 2}
fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int

作為一個(gè)真實(shí)的例子,crypto/sha256包的Sum256函數(shù)對(duì)一個(gè)任意的字節(jié)slice類型的數(shù)據(jù)生成一個(gè)對(duì)應(yīng)的消息摘要。消息摘要有256bit大小,因此對(duì)應(yīng)[32]byte數(shù)組類型。如果兩個(gè)消息摘要是相同的,那么可以認(rèn)為兩個(gè)消息本身也是相同(譯注:理論上有HASH碼碰撞的情況,但是實(shí)際應(yīng)用可以基本忽略);如果消息摘要不同,那么消息本身必然也是不同的。下面的例子用SHA256算法分別生成“x”和“X”兩個(gè)信息的摘要:

gopl.io/ch4/sha256

import "crypto/sha256"

func main() {
    c1 := sha256.Sum256([]byte("x"))
    c2 := sha256.Sum256([]byte("X"))
    fmt.Printf("%x\n%x\n%t\n%T\n", c1, c2, c1 == c2, c1)
    // Output:
    // 2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881
    // 4b68ab3847feda7d6c62c1fbcbeebfa35eab7351ed5e78f4ddadea5df64b8015
    // false
    // [32]uint8
}

上面例子中,兩個(gè)消息雖然只有一個(gè)字符的差異,但是生成的消息摘要?jiǎng)t幾乎有一半的bit位是不相同的。需要注意Printf函數(shù)的%x副詞參數(shù),它用于指定以十六進(jìn)制的格式打印數(shù)組或slice全部的元素,%t副詞參數(shù)是用于打印布爾型數(shù)據(jù),%T副詞參數(shù)是用于顯示一個(gè)值對(duì)應(yīng)的數(shù)據(jù)類型。

當(dāng)調(diào)用一個(gè)函數(shù)的時(shí)候,函數(shù)的每個(gè)調(diào)用參數(shù)將會(huì)被賦值給函數(shù)內(nèi)部的參數(shù)變量,所以函數(shù)參數(shù)變量接收的是一個(gè)復(fù)制的副本,并不是原始調(diào)用的變量。因?yàn)楹瘮?shù)參數(shù)傳遞的機(jī)制導(dǎo)致傳遞大的數(shù)組類型將是低效的,并且對(duì)數(shù)組參數(shù)的任何的修改都是發(fā)生在復(fù)制的數(shù)組上,并不能直接修改調(diào)用時(shí)原始的數(shù)組變量。在這個(gè)方面,Go語(yǔ)言對(duì)待數(shù)組的方式和其它很多編程語(yǔ)言不同,其它編程語(yǔ)言可能會(huì)隱式地將數(shù)組作為引用或指針對(duì)象傳入被調(diào)用的函數(shù)。

當(dāng)然,我們可以顯式地傳入一個(gè)數(shù)組指針,那樣的話函數(shù)通過指針對(duì)數(shù)組的任何修改都可以直接反饋到調(diào)用者。下面的函數(shù)用于給[32]byte類型的數(shù)組清零:

func zero(ptr *[32]byte) {
    for i := range ptr {
        ptr[i] = 0
    }
}

其實(shí)數(shù)組字面值[32]byte{}就可以生成一個(gè)32字節(jié)的數(shù)組。而且每個(gè)數(shù)組的元素都是零值初始化,也就是0。因此,我們可以將上面的zero函數(shù)寫的更簡(jiǎn)潔一點(diǎn):

func zero(ptr *[32]byte) {
    *ptr = [32]byte{}
}

雖然通過指針來傳遞數(shù)組參數(shù)是高效的,而且也允許在函數(shù)內(nèi)部修改數(shù)組的值,但是數(shù)組依然是僵化的類型,因?yàn)閿?shù)組的類型包含了僵化的長(zhǎng)度信息。上面的zero函數(shù)并不能接收指向[16]byte類型數(shù)組的指針,而且也沒有任何添加或刪除數(shù)組元素的方法。由于這些原因,除了像SHA256這類需要處理特定大小數(shù)組的特例外,數(shù)組依然很少用作函數(shù)參數(shù);相反,我們一般使用slice來替代數(shù)組。

練習(xí) 4.1: 編寫一個(gè)函數(shù),計(jì)算兩個(gè)SHA256哈希碼中不同bit的數(shù)目。(參考2.6.2節(jié)的PopCount函數(shù)。)

練習(xí) 4.2: 編寫一個(gè)程序,默認(rèn)情況下打印標(biāo)準(zhǔn)輸入的SHA256編碼,并支持通過命令行flag定制,輸出SHA384或SHA512哈希算法。



以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)