mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-08-05 23:21:51 +00:00
回到简体
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
## 4.1. 數組
|
||||
## 4.1. 数组
|
||||
|
||||
數組是一個由固定長度的特定類型元素組成的序列,一個數組可以由零個或多個元素組成。因爲數組的長度是固定的,因此在Go語言中很少直接使用數組。和數組對應的類型是Slice(切片),它是可以增長和收縮動態序列,slice功能也更靈活,但是要理解slice工作原理的話需要先理解數組。
|
||||
数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此在Go语言中很少直接使用数组。和数组对应的类型是Slice(切片),它是可以增长和收缩动态序列,slice功能也更灵活,但是要理解slice工作原理的话需要先理解数组。
|
||||
|
||||
數組的每個元素可以通過索引下標來訪問,索引下標的范圍是從0開始到數組長度減1的位置。內置的len函數將返迴數組中元素的個數。
|
||||
数组的每个元素可以通过索引下标来访问,索引下标的范围是从0开始到数组长度减1的位置。内置的len函数将返回数组中元素的个数。
|
||||
|
||||
```Go
|
||||
var a [3]int // array of 3 integers
|
||||
@@ -20,7 +20,7 @@ for _, v := range a {
|
||||
}
|
||||
```
|
||||
|
||||
默認情況下,數組的每個元素都被初始化爲元素類型對應的零值,對於數字類型來説就是0。我們也可以使用數組字面值語法用一組值來初始化數組:
|
||||
默认情况下,数组的每个元素都被初始化为元素类型对应的零值,对于数字类型来说就是0。我们也可以使用数组字面值语法用一组值来初始化数组:
|
||||
|
||||
```Go
|
||||
var q [3]int = [3]int{1, 2, 3}
|
||||
@@ -28,30 +28,30 @@ var r [3]int = [3]int{1, 2}
|
||||
fmt.Println(r[2]) // "0"
|
||||
```
|
||||
|
||||
在數組字面值中,如果在數組的長度位置出現的是“...”省略號,則表示數組的長度是根據初始化值的個數來計算。因此,上面q數組的定義可以簡化爲
|
||||
在数组字面值中,如果在数组的长度位置出现的是“...”省略号,则表示数组的长度是根据初始化值的个数来计算。因此,上面q数组的定义可以简化为
|
||||
|
||||
```Go
|
||||
q := [...]int{1, 2, 3}
|
||||
fmt.Printf("%T\n", q) // "[3]int"
|
||||
```
|
||||
|
||||
數組的長度是數組類型的一個組成部分,因此[3]int和[4]int是兩種不同的數組類型。數組的長度必須是常量表達式,因爲數組的長度需要在編譯階段確定。
|
||||
数组的长度是数组类型的一个组成部分,因此[3]int和[4]int是两种不同的数组类型。数组的长度必须是常量表达式,因为数组的长度需要在编译阶段确定。
|
||||
|
||||
```Go
|
||||
q := [3]int{1, 2, 3}
|
||||
q = [4]int{1, 2, 3, 4} // compile error: cannot assign [4]int to [3]int
|
||||
```
|
||||
|
||||
我們將會發現,數組、slice、map和結構體字面值的寫法都很相似。上面的形式是直接提供順序初始化值序列,但是也可以指定一個索引和對應值列表的方式初始化,就像下面這樣:
|
||||
我们将会发现,数组、slice、map和结构体字面值的写法都很相似。上面的形式是直接提供顺序初始化值序列,但是也可以指定一个索引和对应值列表的方式初始化,就像下面这样:
|
||||
|
||||
```Go
|
||||
type Currency int
|
||||
|
||||
const (
|
||||
USD Currency = iota // 美元
|
||||
EUR // 歐元
|
||||
GBP // 英鎊
|
||||
RMB // 人民幣
|
||||
EUR // 欧元
|
||||
GBP // 英镑
|
||||
RMB // 人民币
|
||||
)
|
||||
|
||||
symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"}
|
||||
@@ -59,15 +59,15 @@ symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"}
|
||||
fmt.Println(RMB, symbol[RMB]) // "3 ¥"
|
||||
```
|
||||
|
||||
在這種形式的數組字面值形式中,初始化索引的順序是無關緊要的,而且沒用到的索引可以省略,和前面提到的規則一樣,未指定初始值的元素將用零值初始化。例如,
|
||||
在这种形式的数组字面值形式中,初始化索引的顺序是无关紧要的,而且没用到的索引可以省略,和前面提到的规则一样,未指定初始值的元素将用零值初始化。例如,
|
||||
|
||||
```Go
|
||||
r := [...]int{99: -1}
|
||||
```
|
||||
|
||||
定義了一個含有100個元素的數組r,最後一個元素被初始化爲-1,其它元素都是用0初始化。
|
||||
定义了一个含有100个元素的数组r,最后一个元素被初始化为-1,其它元素都是用0初始化。
|
||||
|
||||
如果一個數組的元素類型是可以相互比較的,那麽數組類型也是可以相互比較的,這時候我們可以直接通過==比較運算符來比較兩個數組,隻有當兩個數組的所有元素都是相等的時候數組才是相等的。不相等比較運算符!=遵循同樣的規則。
|
||||
如果一个数组的元素类型是可以相互比较的,那么数组类型也是可以相互比较的,这时候我们可以直接通过==比较运算符来比较两个数组,只有当两个数组的所有元素都是相等的时候数组才是相等的。不相等比较运算符!=遵循同样的规则。
|
||||
|
||||
```Go
|
||||
a := [2]int{1, 2}
|
||||
@@ -78,7 +78,7 @@ d := [3]int{1, 2}
|
||||
fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int
|
||||
```
|
||||
|
||||
作爲一個眞實的例子,crypto/sha256包的Sum256函數對一個任意的字節slice類型的數據生成一個對應的消息摘要。消息摘要有256bit大小,因此對應[32]byte數組類型。如果兩個消息摘要是相同的,那麽可以認爲兩個消息本身也是相同(譯註:理論上有HASH碼碰撞的情況,但是實際應用可以基本忽略);如果消息摘要不同,那麽消息本身必然也是不同的。下面的例子用SHA256算法分别生成“x”和“X”兩個信息的摘要:
|
||||
作为一个真实的例子,crypto/sha256包的Sum256函数对一个任意的字节slice类型的数据生成一个对应的消息摘要。消息摘要有256bit大小,因此对应[32]byte数组类型。如果两个消息摘要是相同的,那么可以认为两个消息本身也是相同(译注:理论上有HASH码碰撞的情况,但是实际应用可以基本忽略);如果消息摘要不同,那么消息本身必然也是不同的。下面的例子用SHA256算法分别生成“x”和“X”两个信息的摘要:
|
||||
|
||||
<u><i>gopl.io/ch4/sha256</i></u>
|
||||
```Go
|
||||
@@ -96,11 +96,11 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
上面例子中,兩個消息雖然隻有一個字符的差異,但是生成的消息摘要則幾乎有一半的bit位是不相同的。需要註意Printf函數的%x副詞參數,它用於指定以十六進製的格式打印數組或slice全部的元素,%t副詞參數是用於打印布爾型數據,%T副詞參數是用於顯示一個值對應的數據類型。
|
||||
上面例子中,两个消息虽然只有一个字符的差异,但是生成的消息摘要则几乎有一半的bit位是不相同的。需要注意Printf函数的%x副词参数,它用于指定以十六进制的格式打印数组或slice全部的元素,%t副词参数是用于打印布尔型数据,%T副词参数是用于显示一个值对应的数据类型。
|
||||
|
||||
當調用一個函數的時候,函數的每個調用參數將會被賦值給函數內部的參數變量,所以函數參數變量接收的是一個複製的副本,併不是原始調用的變量。因爲函數參數傳遞的機製導致傳遞大的數組類型將是低效的,併且對數組參數的任何的脩改都是發生在複製的數組上,併不能直接脩改調用時原始的數組變量。在這個方面,Go語言對待數組的方式和其它很多編程語言不同,其它編程語言可能會隱式地將數組作爲引用或指針對象傳入被調用的函數。
|
||||
当调用一个函数的时候,函数的每个调用参数将会被赋值给函数内部的参数变量,所以函数参数变量接收的是一个复制的副本,并不是原始调用的变量。因为函数参数传递的机制导致传递大的数组类型将是低效的,并且对数组参数的任何的修改都是发生在复制的数组上,并不能直接修改调用时原始的数组变量。在这个方面,Go语言对待数组的方式和其它很多编程语言不同,其它编程语言可能会隐式地将数组作为引用或指针对象传入被调用的函数。
|
||||
|
||||
當然,我們可以顯式地傳入一個數組指針,那樣的話函數通過指針對數組的任何脩改都可以直接反饋到調用者。下面的函數用於給[32]byte類型的數組清零:
|
||||
当然,我们可以显式地传入一个数组指针,那样的话函数通过指针对数组的任何修改都可以直接反馈到调用者。下面的函数用于给[32]byte类型的数组清零:
|
||||
|
||||
```Go
|
||||
func zero(ptr *[32]byte) {
|
||||
@@ -110,7 +110,7 @@ func zero(ptr *[32]byte) {
|
||||
}
|
||||
```
|
||||
|
||||
其實數組字面值[32]byte{}就可以生成一個32字節的數組。而且每個數組的元素都是零值初始化,也就是0。因此,我們可以將上面的zero函數寫的更簡潔一點:
|
||||
其实数组字面值[32]byte{}就可以生成一个32字节的数组。而且每个数组的元素都是零值初始化,也就是0。因此,我们可以将上面的zero函数写的更简洁一点:
|
||||
|
||||
```Go
|
||||
func zero(ptr *[32]byte) {
|
||||
@@ -118,8 +118,8 @@ func zero(ptr *[32]byte) {
|
||||
}
|
||||
```
|
||||
|
||||
雖然通過指針來傳遞數組參數是高效的,而且也允許在函數內部脩改數組的值,但是數組依然是殭化的類型,因爲數組的類型包含了殭化的長度信息。上面的zero函數併不能接收指向[16]byte類型數組的指針,而且也沒有任何添加或刪除數組元素的方法。由於這些原因,除了像SHA256這類需要處理特定大小數組的特例外,數組依然很少用作函數參數;相反,我們一般使用slice來替代數組。
|
||||
虽然通过指针来传递数组参数是高效的,而且也允许在函数内部修改数组的值,但是数组依然是僵化的类型,因为数组的类型包含了僵化的长度信息。上面的zero函数并不能接收指向[16]byte类型数组的指针,而且也没有任何添加或删除数组元素的方法。由于这些原因,除了像SHA256这类需要处理特定大小数组的特例外,数组依然很少用作函数参数;相反,我们一般使用slice来替代数组。
|
||||
|
||||
**練習 4.1:** 編寫一個函數,計算兩個SHA256哈希碼中不同bit的數目。(參考2.6.2節的PopCount函數。)
|
||||
**练习 4.1:** 编写一个函数,计算两个SHA256哈希码中不同bit的数目。(参考2.6.2节的PopCount函数。)
|
||||
|
||||
**練習 4.2:** 編寫一個程序,默認打印標準輸入的以SHA256哈希碼,也可以通過命令行標準參數選擇SHA384或SHA512哈希算法。
|
||||
**练习 4.2:** 编写一个程序,默认打印标准输入的以SHA256哈希码,也可以通过命令行标准参数选择SHA384或SHA512哈希算法。
|
||||
|
Reference in New Issue
Block a user