Update golang_tutorial_11.md

pull/18/head
RickyWang1020 4 years ago committed by ShaoBo Wan(無尘)
parent 41f3e3863f
commit c9fc5db540

@ -10,7 +10,7 @@
数组是类型相同的元素的集合。例如,整数 5, 8, 9, 79, 76 的集合就构成了一个数组。Go不允许在数组中混合使用不同类型的元素比如整数和字符串 数组是类型相同的元素的集合。例如,整数 5, 8, 9, 79, 76 的集合就构成了一个数组。Go不允许在数组中混合使用不同类型的元素比如整数和字符串
## ##
数组的类型为` [n]T`,其中 `n` 表示数组中元素的个数,`T` 表示数组中元素的类型。元素的个数 `n`也是数组类型的一部分(我们将在稍后详细讨论)。 数组的类型为` [n]T`,其中 `n` 表示数组中元素的个数,`T` 表示数组中元素的类型。元素的个数 `n`也是数组类型的一部分(我们将在稍后详细讨论)。
@ -51,9 +51,9 @@ func main() {
`a[0]`表示数组中的第一个元素。程序的输出为:`[12 78 50]`。 `a[0]`表示数组中的第一个元素。程序的输出为:`[12 78 50]`。
(译者注:可以用下标运算符(`[]`)来访问数组中的元素,下标从 0 开始,例如 `a[0]` 表示数组 a 的第一个元素,`a[1] `表示数组 a 的第二元素,以此类推。) (译者注:可以用下标运算符(`[]`)来访问数组中的元素,下标从 0 开始,例如 `a[0]` 表示数组 a 的第一个元素,`a[1]` 表示数组 a 的第二元素,以此类推。)
可以利用**速记声明shorthand declaration**的方式来创建同样的数组: 可以利用 **速记声明shorthand declaration** 的方式来创建同样的数组:
```golang ```golang
package main package main
@ -87,7 +87,7 @@ func main() {
} }
``` ```
上面程序的第 8 行:`a := [3]int{12} `声明了一个长度为 3 的数组,但是只提供了一个初值 12。剩下的两个元素被自动赋值为 0。程序 的输出为:`[12 0 0]`。 上面程序的第 8 行:`a := [3]int{12}` 声明了一个长度为 3 的数组,但是只提供了一个初值 12。剩下的两个元素被自动赋值为 0。程序 的输出为:`[12 0 0]`。
在声明数组时你可以忽略数组的长度并用` ... `代替,让编译器为你自动推导数组的长度。比如下面的程序: 在声明数组时你可以忽略数组的长度并用` ... `代替,让编译器为你自动推导数组的长度。比如下面的程序:
@ -104,7 +104,7 @@ func main() {
} }
``` ```
上面已经提到,数组的长度是数组类型的一部分。因此 `[5]int``[25]int`是两个不同类型的数组。正是因为如此,一个数组不能动态改变长度。不要担心这个限制,因为切片(`slices`)可以弥补这个不足。 上面已经提到,数组的长度是数组类型的一部分。因此 `[5]int``[25]int` 是两个不同类型的数组。正是因为如此,一个数组不能动态改变长度。不要担心这个限制,因为切片(`slices`)可以弥补这个不足。
```golang ```golang
package main package main
@ -116,7 +116,7 @@ func main() {
} }
``` ```
在上面程序的第 6 行,我们试图将一个` [3]int `类型的数组赋值给一个 `[5]int `类型的数组,这是不允许的。编译会报错:`main.go:6: cannot use a (type [3]int) as type [5]int in assignment。` 在上面程序的第 6 行,我们试图将一个 `[3]int` 类型的数组赋值给一个 `[5]int` 类型的数组,这是不允许的。编译会报错:`main.go:6: cannot use a (type [3]int) as type [5]int in assignment。`
## 数组是值类型 ## 数组是值类型
@ -189,7 +189,7 @@ func main() {
## 使用 range 遍历数组 ## 使用 range 遍历数组
`for `循环可以用来遍历数组中的元素: `for` 循环可以用来遍历数组中的元素:
```golang ```golang
package main package main
@ -212,7 +212,7 @@ func main() {
3 th element of a is 78.00 3 th element of a is 78.00
``` ```
Go 提供了一个更简单,更简洁的遍历数组的方法:使用 `range for`range 返回数组的索引和索引对应的值。让我们用 range for 重写上面的程序(除此之外我们还计算了数组元素的总和)。 Go 提供了一个更简单,更简洁的遍历数组的方法:使用 range 形式的 for 循环。`range` 返回数组的索引和索引对应的值。让我们用 range for 重写上面的程序(除此之外我们还计算了数组元素的总和)。
```golang ```golang
package main package main
@ -247,7 +247,7 @@ for _, v := range a { //ignores index
} }
``` ```
上面的代码忽略了索引。同样的,也可以忽略值。 上面的代码忽略了索引。用类似的方法,也可以忽略值。
## 多维数组 ## 多维数组
@ -288,7 +288,7 @@ func main() {
} }
``` ```
上面的程序中,第 17 行利用速记声明创建了一个二维数组 a。第 20 行的逗号是必须的,这是因为词法分析器会根据一些简单的规则自动插入分号。如果你想了解更多请阅读https://golang.org/doc/effective_go.html#semicolons 。 上面的程序中,第 17 行利用速记声明创建了一个二维数组 a。第 20 行的逗号是必须的因为词法分析器会根据一些简单的规则自动插入分号。如果你想了解更多请阅读https://golang.org/doc/effective_go.html#semicolons 。
在第 23 行声明了另一个二维数组 b并通过索引的方式给数组 b 中的每一个元素赋值。这是初始化二维数组的另一种方式。 在第 23 行声明了另一个二维数组 b并通过索引的方式给数组 b 中的每一个元素赋值。这是初始化二维数组的另一种方式。
@ -312,7 +312,7 @@ AT&T T-Mobile
## 创建切片 ## 创建切片
元素类型为 `T `的切片表示为: `[]T` 元素类型为 `T` 的切片表示为: `[]T`
```golang ```golang
package main package main
@ -328,7 +328,7 @@ func main() {
} }
``` ```
通过 `a[start:end] `这样的语法创建了一个从` a[start]` 到 `a[end -1]` 的切片。在上面的程序中,第 9 行` a[1:4]` 创建了一个从 `a[1]``a[3]` 的切片。因此 b 的值为:`[77 78 79]`。 通过 `a[start:end]` 这样的语法创建了一个从 `a[start]``a[end -1]` 的切片。在上面的程序中,第 9 行 `a[1:4]` 创建了一个从 `a[1]``a[3]` 的切片。因此 b 的值为:`[77 78 79]`。
下面是创建切片的另一种方式: 下面是创建切片的另一种方式:
@ -345,7 +345,7 @@ func main() {
} }
``` ```
在上面的程序中,第 9 行 `c := []int{6, 7, 8} `创建了一个长度为 3 的 int 数组,并返回一个切片给 c。 在上面的程序中,第 9 行 `c := []int{6, 7, 8}` 创建了一个长度为 3 的 int 数组,并返回一个切片给 c。
## 修改切片 ## 修改切片
@ -397,7 +397,7 @@ func main() {
} }
``` ```
可以看到,在第 9 行,` numa[:] `中缺少了开始和结束的索引值,这种情况下开始和结束的索引值默认为 `0` 和` len(numa)`。这里 `nums1``nums2` 共享了同一个数组。程序的输出为: 可以看到,在第 9 行,`numa[:]` 中缺少了开始和结束的索引值,这种情况下开始和结束的索引值默认为 `0` `len(numa)`。这里 `nums1``nums2` 共享了同一个数组。程序的输出为:
```golang ```golang
array before change 1 [78 79 80] array before change 1 [78 79 80]
@ -429,11 +429,11 @@ func main() {
} }
``` ```
在上面的程序中,创建了一个以 fruitarray 为底层数组,索引从 1 到 3 的切片 `fruitslice`因此 fruitslice 长度为 2。 在上面的程序中,创建了一个以 `fruitarray` 为底层数组,索引从 1 到 2 的切片 `fruitslice`因此 fruitslice 长度为 2。
`fruitarray` 的长度是 7。`fruiteslice` 是从 `fruitarray` 的索引 1 开始的。因此 `fruiteslice` 的容量是从 `fruitarray` 的第 1 个元素开始算起的数组中的元素个数,这个值是 6。因此 `fruitslice` 的容量是 6。程序的输出为`length of slice 2 capacity 6 ` `fruitarray` 的长度是 7。`fruiteslice` 是从 `fruitarray` 的索引 1 开始的。因此 `fruiteslice` 的容量是从 `fruitarray` 的第 1 个元素开始算起的数组中的元素个数,这个值是 6。因此 `fruitslice` 的容量是 6。程序的输出为`length of slice 2 capacity 6`
切片的长度可以动态的改变(最大为其容量)。任何超出最大容量的操作都会发生运行时错误。 切片的长度可以动态地改变(最大长度为其容量)。任何超出切片容量的操作都会发生运行错误。
```golang ```golang
package main package main
@ -460,7 +460,7 @@ After re-slicing length is 6 and capacity is 6
## 用 make 创建切片 ## 用 make 创建切片
内置函数` func make([]T, len, cap) []T` 可以用来创建切片,该函数接受**长度**和**容量**作为参数,返回切片。容量是可选的,默认与长度相同。使用 make 函数将会创建一个数组并返回它的切片。 内置函数 `func make([]T, len, cap) []T` 可以用来创建切片,该函数接受**长度**和**容量**作为参数,返回切片。容量是可选的,默认与长度相同。使用 make 函数将会创建一个数组并返回它的切片。
```golang ```golang
package main package main
@ -483,7 +483,7 @@ func main() {
`x …T` 表示 append 函数可以接受的参数个数是可变的。这种函数叫做变参函数。 `x …T` 表示 append 函数可以接受的参数个数是可变的。这种函数叫做变参函数。
你可能会问一个问题:如果切片是建立在数组之上的,而数组本身不能改变长度,那么切片是如何动态改变长度的呢?实际发生的情况是,当新元素通过调用 append 函数追加到切片末尾时如果超出了容量append 内部会创建一个新的数组。并将原有数组的元素拷贝给这个新的数组最后返回建立在这个新数组上的切片。这个新切片的容量是旧切片的二倍译者注当超出切片的容量时append 将会在其内部创建新的数组,该数组的大小是原切片容量的 2 倍。最后 append 返回这个数组的全切片,即从 0 到 length - 1 的切片)。下面的程序使事情变得明朗 你可能会问一个问题:如果切片是建立在数组之上的,而数组本身不能改变长度,那么切片是如何动态改变长度的呢?实际发生的情况是,当新元素通过调用 append 函数追加到切片末尾时如果超出了容量append 内部会创建一个新的数组。并将原有数组的元素拷贝给这个新的数组最后返回建立在这个新数组上的切片。这个新切片的容量是旧切片的二倍译者注当超出切片的容量时append 将会在其内部创建新的数组,该数组的大小是原切片容量的 2 倍。最后 append 返回这个数组的全切片,即从 0 到 length - 1 的切片)。下面的程序会帮助你理解这一知识点
```golang ```golang
package main package main
@ -500,7 +500,7 @@ func main() {
} }
``` ```
在上面的程序中cars 的容量开始时为 3。在第 10 行我们追加了一个新的元素给 `cars`,并将 `append(cars, "Toyota") `的返回值重新复制给 cars。现在 cars 的容量翻倍,变为 6。上面的程序输出为 在上面的程序中cars 的容量开始时为 3。在第 10 行我们追加了一个新的元素给 `cars`,并将 `append(cars, "Toyota")` 的返回值重新复制给 cars。现在 cars 的容量翻倍,变为 6。上面的程序输出为
```golang ```golang
cars: [Ferrari Honda Ford] has old length 3 and capacity 3 cars: [Ferrari Honda Ford] has old length 3 and capacity 3
@ -588,7 +588,7 @@ func main() {
} }
``` ```
在上面的程序中,第 17 行将切片中的每个元素的值减2。在函数调用之后打印切片的的内容发现切片内容发生了改变。你可以回想一下这不同于一个数组对函数内部的数组所做的更改在函数外不可见。上面的程序输出如下 在上面的程序中,第 17 行将切片中的每个元素的值减 2。在函数调用之后打印切片的的内容发现切片内容发生了改变。你可以回想一下这不同于一个数组对函数内部的数组所做的更改在函数外不可见。上面的程序输出如下
```golang ```golang
array before function call [8 7 6] array before function call [8 7 6]
@ -634,7 +634,7 @@ Go Rust
切片保留对底层数组的引用。只要切片存在于内存中,数组就不能被垃圾回收。这在内存管理方便可能是值得关注的。假设我们有一个非常大的数组,而我们只需要处理它的一小部分,为此我们创建这个数组的一个切片,并处理这个切片。这里要注意的事情是,数组仍然存在于内存中,因为切片正在引用它。 切片保留对底层数组的引用。只要切片存在于内存中,数组就不能被垃圾回收。这在内存管理方便可能是值得关注的。假设我们有一个非常大的数组,而我们只需要处理它的一小部分,为此我们创建这个数组的一个切片,并处理这个切片。这里要注意的事情是,数组仍然存在于内存中,因为切片正在引用它。
解决该问题的一个方法是使用 copy 函数 `func copy(dst, src []T) int `来创建该切片的一个拷贝。这样我们就可以使用这个新的切片,原来的数组可以被垃圾回收。 解决该问题的一个方法是使用 copy 函数 `func copy(dst, src []T) int` 来创建该切片的一个拷贝。这样我们就可以使用这个新的切片,原来的数组可以被垃圾回收。
```golang ```golang
package main package main
@ -656,7 +656,7 @@ func main() {
} }
``` ```
在上面程序中,第 9 行 `neededCountries := countries[:len(countries)-2] `创建一个底层数组为 `countries` 并排除最后两个元素的切片。第 11 行将 `neededCountries` 拷贝到 `countriesCpy` 并在下一行返回 `countriesCpy`。现在数组 `countries` 可以被垃圾回收,因为 `neededCountries` 不再被引用。 在上面程序中,第 9 行 `neededCountries := countries[:len(countries)-2]` 创建一个底层数组为 `countries` 并排除最后两个元素的切片。第 11 行将 `neededCountries` 拷贝到 `countriesCpy` 并在下一行返回 `countriesCpy`。现在数组 `countries` 可以被垃圾回收,因为 `neededCountries` 不再被引用。
我(原文作者)已经将我们讨论的所有概念汇总到一个程序中,你可以从 [github](https://github.com/golangbot/arraysandslices) 下载。 我(原文作者)已经将我们讨论的所有概念汇总到一个程序中,你可以从 [github](https://github.com/golangbot/arraysandslices) 下载。
@ -666,9 +666,9 @@ func main() {
[深入解析 Go 中 Slice 底层实现](https://halfrost.com/go_slice/) [深入解析 Go 中 Slice 底层实现](https://halfrost.com/go_slice/)
###### 创建(定义)数组 ###### 创建(定义)数组
数组在Go中是值类型而不是引用其他语言的数组则是引用类型 数组在Go中是值类型而不是引用其他语言的数组则是引用类型
PS切片slice是一个引用类型
数组不是统一的类型,大小不同的数组是不可以比较的 PS切片slice是一个引用类型。数组不是统一的类型,大小不同的数组是不可以比较的,不同数组类型是不可以比较的
不同数组类型是不可以比较的
```golang ```golang
var a[2]int var a[2]int
var b[3]string var b[3]string
@ -689,9 +689,9 @@ var d = [...]int{19:90} // 尽可能的满足索引值得数组
a := [...]int{19:100} a := [...]int{19:100}
// 指向数组的指针 // 指向数组的指针
var p *[20]int = &a //长度为20的int型数组这里的数组长度`20`必须和a数组长度相等 var p *[20]int = &a //长度为20的int型数组这里的数组长度 `20` 必须和a数组长度相等
fmt.Println(p) //以上表示取这样一个数组的地址 fmt.Println(p) //以上表示取这样一个数组的地址
// 打印结果:&[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 19] // 打印结果:&[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100]
//指向指针的数组 //指向指针的数组
x , y := 1, 2 x , y := 1, 2
@ -702,7 +702,7 @@ fmt.Println("pp is ",pp) // 打印结果pp is [0xc420012128 0xc420012130]
```golang ```golang
ppp := new([10]int) ppp := new([10]int)
fmt.Println("----ppp-------",ppp); //输出结果:`&[0 0 0 0 0 0 0 0 0 0]` fmt.Println("----ppp-------",ppp); //输出结果:`----ppp------- &[0 0 0 0 0 0 0 0 0 0]`
//以上为指向数组的指针 //以上为指向数组的指针
``` ```
@ -712,12 +712,12 @@ fmt.Println("----ppp-------",ppp); //输出结果:`&[0 0 0 0 0 0 0 0 0 0]`
// 第一种方式 // 第一种方式
n := [10]int{} n := [10]int{}
n[1] = 10 n[1] = 10
fmt.Println("-----1------",n) // 输出:`[0 10 0 0 0 0 0 0 0 0]` fmt.Println("-----1------",n) // 输出:`-----1------ [0 10 0 0 0 0 0 0 0 0]`
// 第二种方式 // 第二种方式
m := new([10]int) m := new([10]int)
m[1] = 20 m[1] = 20
fmt.Println("-----2------",m) // 输出:`&[0 20 0 0 0 0 0 0 0 0]` fmt.Println("-----2------",m) // 输出:`-----2------ &[0 20 0 0 0 0 0 0 0 0]`
``` ```
###### 多维数组 ###### 多维数组
@ -804,7 +804,7 @@ func main() {
s7 := []byte{'a','b','c','d','e','f','g','h','i','j','k'} // 切片底层对应的数组 s7 := []byte{'a','b','c','d','e','f','g','h','i','j','k'} // 切片底层对应的数组
slice_a := s7[2:5] slice_a := s7[2:5]
fmt.Println(slice_a) // 输出的assica码 值 [99 100 101] fmt.Println(slice_a) // 输出的ascii码 值 [99 100 101]
fmt.Println(string(slice_a)) // 格式化为字符串输出 fmt.Println(string(slice_a)) // 格式化为字符串输出
fmt.Println(len(slice_a),cap(slice_a)) fmt.Println(len(slice_a),cap(slice_a))
@ -817,7 +817,7 @@ func main() {
s8 = append(s8, 12, 48) s8 = append(s8, 12, 48)
fmt.Printf("%v %p", s8, s8) // 格式化打印值和内存地址:[0 0 0 12 48] 0xc042074030 fmt.Printf("%v %p", s8, s8) // 格式化打印值和内存地址:[0 0 0 12 48] 0xc042074030
// 追加的元素如果没有超切片容量,则切片的地址是不变的,否则内存地址会变 // 追加的元素如果没有超切片容量,则切片的地址是不变的,否则内存地址会变
s8 = append(s8, 66, 88) s8 = append(s8, 66, 88)
fmt.Printf("%v %p\n", s8, s8) // [0 0 0 12 48 66 88] 0xc042048060 fmt.Printf("%v %p\n", s8, s8) // [0 0 0 12 48 66 88] 0xc042048060
@ -828,7 +828,7 @@ func main() {
fmt.Println(s9) //[33 44 55 4 5 6 7] fmt.Println(s9) //[33 44 55 4 5 6 7]
copy(s10,s9) copy(s10,s9)
fmt.Println(s10) // [33 44 55] fmt.Println(s10) // [1 2 3]
copy(s9[2:4],s10[0:2]) copy(s9[2:4],s10[0:2])
fmt.Println(s9) // [1 2 33 44 5 6 7] fmt.Println(s9) // [1 2 33 44 5 6 7]

Loading…
Cancel
Save