From c9fc5db5405658c217db6b0a3668f3c0da25da3b Mon Sep 17 00:00:00 2001 From: RickyWang1020 <50431019+RickyWang1020@users.noreply.github.com> Date: Fri, 21 Aug 2020 14:53:58 +0800 Subject: [PATCH] Update golang_tutorial_11.md --- docs/golang_tutorial_11.md | 68 +++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/docs/golang_tutorial_11.md b/docs/golang_tutorial_11.md index 3816dc1..c3e787b 100644 --- a/docs/golang_tutorial_11.md +++ b/docs/golang_tutorial_11.md @@ -10,7 +10,7 @@ 数组是类型相同的元素的集合。例如,整数 5, 8, 9, 79, 76 的集合就构成了一个数组。Go不允许在数组中混合使用不同类型的元素(比如整数和字符串)。 -## 申明 +## 声明 数组的类型为` [n]T`,其中 `n` 表示数组中元素的个数,`T` 表示数组中元素的类型。元素的个数 `n`也是数组类型的一部分(我们将在稍后详细讨论)。 @@ -51,9 +51,9 @@ func main() { `a[0]`表示数组中的第一个元素。程序的输出为:`[12 78 50]`。 -(译者注:可以用下标运算符(`[]`)来访问数组中的元素,下标从 0 开始,例如 `a[0]` 表示数组 a 的第一个元素,`a[1] `表示数组 a 的第二元素,以此类推。) +(译者注:可以用下标运算符(`[]`)来访问数组中的元素,下标从 0 开始,例如 `a[0]` 表示数组 a 的第一个元素,`a[1]` 表示数组 a 的第二个元素,以此类推。) -可以利用**速记声明(shorthand declaration)**的方式来创建同样的数组: +可以利用 **速记声明(shorthand declaration)** 的方式来创建同样的数组: ```golang 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 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 遍历数组 -`for `循环可以用来遍历数组中的元素: +`for` 循环可以用来遍历数组中的元素: ```golang package main @@ -212,7 +212,7 @@ func main() { 3 th element of a is 78.00 ``` -Go 提供了一个更简单,更简洁的遍历数组的方法:使用 `range for`。range 返回数组的索引和索引对应的值。让我们用 range for 重写上面的程序(除此之外我们还计算了数组元素的总和)。 +Go 提供了一个更简单,更简洁的遍历数组的方法:使用 range 形式的 for 循环。`range` 返回数组的索引和索引对应的值。让我们用 range for 重写上面的程序(除此之外我们还计算了数组元素的总和)。 ```golang 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 中的每一个元素赋值。这是初始化二维数组的另一种方式。 @@ -312,7 +312,7 @@ AT&T T-Mobile ## 创建切片 -元素类型为 `T `的切片表示为: `[]T`。 +元素类型为 `T` 的切片表示为: `[]T`。 ```golang 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 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 package main @@ -460,7 +460,7 @@ After re-slicing length is 6 and capacity is 6 ## 用 make 创建切片 -内置函数` func make([]T, len, cap) []T` 可以用来创建切片,该函数接受**长度**和**容量**作为参数,返回切片。容量是可选的,默认与长度相同。使用 make 函数将会创建一个数组并返回它的切片。 +内置函数 `func make([]T, len, cap) []T` 可以用来创建切片,该函数接受**长度**和**容量**作为参数,返回切片。容量是可选的,默认与长度相同。使用 make 函数将会创建一个数组并返回它的切片。 ```golang package main @@ -483,7 +483,7 @@ func main() { `x …T` 表示 append 函数可以接受的参数个数是可变的。这种函数叫做变参函数。 -你可能会问一个问题:如果切片是建立在数组之上的,而数组本身不能改变长度,那么切片是如何动态改变长度的呢?实际发生的情况是,当新元素通过调用 append 函数追加到切片末尾时,如果超出了容量,append 内部会创建一个新的数组。并将原有数组的元素被拷贝给这个新的数组,最后返回建立在这个新数组上的切片。这个新切片的容量是旧切片的二倍(译者注:当超出切片的容量时,append 将会在其内部创建新的数组,该数组的大小是原切片容量的 2 倍。最后 append 返回这个数组的全切片,即从 0 到 length - 1 的切片)。下面的程序使事情变得明朗: +你可能会问一个问题:如果切片是建立在数组之上的,而数组本身不能改变长度,那么切片是如何动态改变长度的呢?实际发生的情况是,当新元素通过调用 append 函数追加到切片末尾时,如果超出了容量,append 内部会创建一个新的数组。并将原有数组的元素拷贝给这个新的数组,最后返回建立在这个新数组上的切片。这个新切片的容量是旧切片的二倍(译者注:当超出切片的容量时,append 将会在其内部创建新的数组,该数组的大小是原切片容量的 2 倍。最后 append 返回这个数组的全切片,即从 0 到 length - 1 的切片)。下面的程序会帮助你理解这一知识点: ```golang 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 cars: [Ferrari Honda Ford] has old length 3 and capacity 3 @@ -588,7 +588,7 @@ func main() { } ``` -在上面的程序中,第 17 行将切片中的每个元素的值减2。在函数调用之后打印切片的的内容,发现切片内容发生了改变。你可以回想一下,这不同于一个数组,对函数内部的数组所做的更改在函数外不可见。上面的程序输出如下: +在上面的程序中,第 17 行将切片中的每个元素的值减 2。在函数调用之后打印切片的的内容,发现切片内容发生了改变。你可以回想一下,这不同于一个数组,对函数内部的数组所做的更改在函数外不可见。上面的程序输出如下: ```golang 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 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) 下载。 @@ -666,9 +666,9 @@ func main() { [深入解析 Go 中 Slice 底层实现](https://halfrost.com/go_slice/) ###### 创建(定义)数组 数组在Go中是值类型,而不是引用(其他语言的数组则是引用类型) -PS:切片(slice)是一个引用类型 -数组不是统一的类型,大小不同的数组是不可以比较的 -不同数组类型是不可以比较的 + +PS:切片(slice)是一个引用类型。数组不是统一的类型,大小不同的数组是不可以比较的,不同数组类型是不可以比较的 + ```golang var a[2]int var b[3]string @@ -689,9 +689,9 @@ var d = [...]int{19:90} // 尽可能的满足索引值得数组 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) //以上表示取这样一个数组的地址 -// 打印结果:&[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 @@ -702,7 +702,7 @@ fmt.Println("pp is ",pp) // 打印结果:pp is [0xc420012128 0xc420012130] ```golang 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[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[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'} // 切片底层对应的数组 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(len(slice_a),cap(slice_a)) @@ -817,7 +817,7 @@ func main() { s8 = append(s8, 12, 48) fmt.Printf("%v %p", s8, s8) // 格式化打印值和内存地址:[0 0 0 12 48] 0xc042074030 - // 追加的元素如果没有超多切片容量,则切片的地址是不变的,否则内存地址会变 + // 追加的元素如果没有超过切片容量,则切片的地址是不变的,否则内存地址会变 s8 = append(s8, 66, 88) 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] copy(s10,s9) - fmt.Println(s10) // [33 44 55] + fmt.Println(s10) // [1 2 3] copy(s9[2:4],s10[0:2]) fmt.Println(s9) // [1 2 33 44 5 6 7]