10
10
11
11
数组是类型相同的元素的集合。例如,整数 5, 8, 9, 79, 76 的集合就构成了一个数组。Go不允许在数组中混合使用不同类型的元素(比如整数和字符串)。
12
12
13
- ## 申明
13
+ ## 声明
14
14
15
15
数组的类型为` [n]T ` ,其中 ` n ` 表示数组中元素的个数,` T ` 表示数组中元素的类型。元素的个数 ` n ` 也是数组类型的一部分(我们将在稍后详细讨论)。
16
16
@@ -51,9 +51,9 @@ func main() {
51
51
52
52
` a[0] ` 表示数组中的第一个元素。程序的输出为:` [12 78 50] ` 。
53
53
54
- (译者注:可以用下标运算符(` [] ` )来访问数组中的元素,下标从 0 开始,例如 ` a[0] ` 表示数组 a 的第一个元素,` a[1] ` 表示数组 a 的第二元素 ,以此类推。)
54
+ (译者注:可以用下标运算符(` [] ` )来访问数组中的元素,下标从 0 开始,例如 ` a[0] ` 表示数组 a 的第一个元素,` a[1] ` 表示数组 a 的第二个元素 ,以此类推。)
55
55
56
- 可以利用** 速记声明(shorthand declaration)** 的方式来创建同样的数组:
56
+ 可以利用 ** 速记声明(shorthand declaration)** 的方式来创建同样的数组:
57
57
58
58
``` golang
59
59
package main
@@ -87,7 +87,7 @@ func main() {
87
87
}
88
88
```
89
89
90
- 上面程序的第 8 行:` a := [3]int{12} ` 声明了一个长度为 3 的数组,但是只提供了一个初值 12。剩下的两个元素被自动赋值为 0。程序 的输出为:` [12 0 0] ` 。
90
+ 上面程序的第 8 行:` a := [3]int{12} ` 声明了一个长度为 3 的数组,但是只提供了一个初值 12。剩下的两个元素被自动赋值为 0。程序 的输出为:` [12 0 0] ` 。
91
91
92
92
在声明数组时你可以忽略数组的长度并用` ... ` 代替,让编译器为你自动推导数组的长度。比如下面的程序:
93
93
@@ -104,7 +104,7 @@ func main() {
104
104
}
105
105
```
106
106
107
- 上面已经提到,数组的长度是数组类型的一部分。因此 ` [5]int ` 和 ` [25]int ` 是两个不同类型的数组。正是因为如此,一个数组不能动态改变长度。不要担心这个限制,因为切片(` slices ` )可以弥补这个不足。
107
+ 上面已经提到,数组的长度是数组类型的一部分。因此 ` [5]int ` 和 ` [25]int ` 是两个不同类型的数组。正是因为如此,一个数组不能动态改变长度。不要担心这个限制,因为切片(` slices ` )可以弥补这个不足。
108
108
109
109
``` golang
110
110
package main
@@ -116,7 +116,7 @@ func main() {
116
116
}
117
117
```
118
118
119
- 在上面程序的第 6 行,我们试图将一个` [3]int ` 类型的数组赋值给一个 ` [5]int ` 类型的数组,这是不允许的。编译会报错:` main.go:6: cannot use a (type [3]int) as type [5]int in assignment。 `
119
+ 在上面程序的第 6 行,我们试图将一个 ` [3]int ` 类型的数组赋值给一个 ` [5]int ` 类型的数组,这是不允许的。编译会报错:` main.go:6: cannot use a (type [3]int) as type [5]int in assignment。 `
120
120
121
121
## 数组是值类型
122
122
@@ -189,7 +189,7 @@ func main() {
189
189
190
190
## 使用 range 遍历数组
191
191
192
- ` for ` 循环可以用来遍历数组中的元素:
192
+ ` for ` 循环可以用来遍历数组中的元素:
193
193
194
194
``` golang
195
195
package main
@@ -212,7 +212,7 @@ func main() {
212
212
3 th element of a is 78.00
213
213
```
214
214
215
- Go 提供了一个更简单,更简洁的遍历数组的方法:使用 ` range for ` 。 range 返回数组的索引和索引对应的值。让我们用 range for 重写上面的程序(除此之外我们还计算了数组元素的总和)。
215
+ Go 提供了一个更简单,更简洁的遍历数组的方法:使用 range 形式的 for 循环。 ` range ` 返回数组的索引和索引对应的值。让我们用 range for 重写上面的程序(除此之外我们还计算了数组元素的总和)。
216
216
``` golang
217
217
package main
218
218
@@ -247,7 +247,7 @@ for _, v := range a { //ignores index
247
247
}
248
248
```
249
249
250
- 上面的代码忽略了索引。同样的 ,也可以忽略值。
250
+ 上面的代码忽略了索引。用类似的方法 ,也可以忽略值。
251
251
252
252
## 多维数组
253
253
@@ -288,7 +288,7 @@ func main() {
288
288
}
289
289
```
290
290
291
- 上面的程序中,第 17 行利用速记声明创建了一个二维数组 a。第 20 行的逗号是必须的,这是因为词法分析器会根据一些简单的规则自动插入分号 。如果你想了解更多,请阅读:https://golang.org/doc/effective_go.html#semicolons 。
291
+ 上面的程序中,第 17 行利用速记声明创建了一个二维数组 a。第 20 行的逗号是必须的,因为词法分析器会根据一些简单的规则自动插入分号 。如果你想了解更多,请阅读:https://golang.org/doc/effective_go.html#semicolons 。
292
292
293
293
在第 23 行声明了另一个二维数组 b,并通过索引的方式给数组 b 中的每一个元素赋值。这是初始化二维数组的另一种方式。
294
294
@@ -312,7 +312,7 @@ AT&T T-Mobile
312
312
313
313
## 创建切片
314
314
315
- 元素类型为 ` T ` 的切片表示为: ` []T ` 。
315
+ 元素类型为 ` T ` 的切片表示为: ` []T ` 。
316
316
317
317
``` golang
318
318
package main
@@ -328,7 +328,7 @@ func main() {
328
328
}
329
329
```
330
330
331
- 通过 ` a[start:end] ` 这样的语法创建了一个从` a[start]` 到 ` a[end -1] ` 的切片。在上面的程序中,第 9 行` a[1:4]` 创建了一个从 ` a[1] ` 到 ` a[3] ` 的切片。因此 b 的值为:` [77 78 79] ` 。
331
+ 通过 ` a[start:end] ` 这样的语法创建了一个从 ` a[start] ` 到 ` a[end -1] ` 的切片。在上面的程序中,第 9 行 ` a[1:4] ` 创建了一个从 ` a[1] ` 到 ` a[3] ` 的切片。因此 b 的值为:` [77 78 79] ` 。
332
332
333
333
下面是创建切片的另一种方式:
334
334
@@ -345,7 +345,7 @@ func main() {
345
345
}
346
346
```
347
347
348
- 在上面的程序中,第 9 行 ` c := []int{6, 7, 8} ` 创建了一个长度为 3 的 int 数组,并返回一个切片给 c。
348
+ 在上面的程序中,第 9 行 ` c := []int{6, 7, 8} ` 创建了一个长度为 3 的 int 数组,并返回一个切片给 c。
349
349
350
350
## 修改切片
351
351
@@ -397,7 +397,7 @@ func main() {
397
397
}
398
398
```
399
399
400
- 可以看到,在第 9 行,` numa[:] ` 中缺少了开始和结束的索引值,这种情况下开始和结束的索引值默认为 ` 0 ` 和` len(numa)` 。这里 ` nums1 ` 和 ` nums2 ` 共享了同一个数组。程序的输出为:
400
+ 可以看到,在第 9 行,` numa[:] ` 中缺少了开始和结束的索引值,这种情况下开始和结束的索引值默认为 ` 0 ` 和 ` len(numa) ` 。这里 ` nums1 ` 和 ` nums2 ` 共享了同一个数组。程序的输出为:
401
401
402
402
``` golang
403
403
array before change 1 [78 79 80 ]
@@ -429,11 +429,11 @@ func main() {
429
429
}
430
430
```
431
431
432
- 在上面的程序中,创建了一个以 fruitarray 为底层数组,索引从 1 到 3 的切片 ` fruitslice ` 。 因此 fruitslice 长度为 2。
432
+ 在上面的程序中,创建了一个以 ` fruitarray ` 为底层数组,索引从 1 到 2 的切片 ` fruitslice ` , 因此 fruitslice 长度为 2。
433
433
434
- ` fruitarray ` 的长度是 7。` fruiteslice ` 是从 ` fruitarray ` 的索引 1 开始的。因此 ` fruiteslice ` 的容量是从 ` fruitarray ` 的第 1 个元素开始算起的数组中的元素个数,这个值是 6。因此 ` fruitslice ` 的容量是 6。程序的输出为:` length of slice 2 capacity 6 `
434
+ ` fruitarray ` 的长度是 7。` fruiteslice ` 是从 ` fruitarray ` 的索引 1 开始的。因此 ` fruiteslice ` 的容量是从 ` fruitarray ` 的第 1 个元素开始算起的数组中的元素个数,这个值是 6。因此 ` fruitslice ` 的容量是 6。程序的输出为:` length of slice 2 capacity 6 `
435
435
436
- 切片的长度可以动态的改变(最大为其容量)。任何超出最大容量的操作都会发生运行时错误 。
436
+ 切片的长度可以动态地改变(最大长度为其容量)。任何超出切片容量的操作都会发生运行错误 。
437
437
438
438
``` golang
439
439
package main
@@ -460,7 +460,7 @@ After re-slicing length is 6 and capacity is 6
460
460
461
461
## 用 make 创建切片
462
462
463
- 内置函数` func make([]T, len, cap) []T` 可以用来创建切片,该函数接受** 长度** 和** 容量** 作为参数,返回切片。容量是可选的,默认与长度相同。使用 make 函数将会创建一个数组并返回它的切片。
463
+ 内置函数 ` func make([]T, len, cap) []T ` 可以用来创建切片,该函数接受** 长度** 和** 容量** 作为参数,返回切片。容量是可选的,默认与长度相同。使用 make 函数将会创建一个数组并返回它的切片。
464
464
465
465
``` golang
466
466
package main
@@ -483,7 +483,7 @@ func main() {
483
483
484
484
` x …T ` 表示 append 函数可以接受的参数个数是可变的。这种函数叫做变参函数。
485
485
486
- 你可能会问一个问题:如果切片是建立在数组之上的,而数组本身不能改变长度,那么切片是如何动态改变长度的呢?实际发生的情况是,当新元素通过调用 append 函数追加到切片末尾时,如果超出了容量,append 内部会创建一个新的数组。并将原有数组的元素被拷贝给这个新的数组 ,最后返回建立在这个新数组上的切片。这个新切片的容量是旧切片的二倍(译者注:当超出切片的容量时,append 将会在其内部创建新的数组,该数组的大小是原切片容量的 2 倍。最后 append 返回这个数组的全切片,即从 0 到 length - 1 的切片)。下面的程序使事情变得明朗 :
486
+ 你可能会问一个问题:如果切片是建立在数组之上的,而数组本身不能改变长度,那么切片是如何动态改变长度的呢?实际发生的情况是,当新元素通过调用 append 函数追加到切片末尾时,如果超出了容量,append 内部会创建一个新的数组。并将原有数组的元素拷贝给这个新的数组 ,最后返回建立在这个新数组上的切片。这个新切片的容量是旧切片的二倍(译者注:当超出切片的容量时,append 将会在其内部创建新的数组,该数组的大小是原切片容量的 2 倍。最后 append 返回这个数组的全切片,即从 0 到 length - 1 的切片)。下面的程序会帮助你理解这一知识点 :
487
487
488
488
``` golang
489
489
package main
@@ -500,7 +500,7 @@ func main() {
500
500
}
501
501
```
502
502
503
- 在上面的程序中,cars 的容量开始时为 3。在第 10 行我们追加了一个新的元素给 ` cars ` ,并将 ` append(cars, "Toyota") ` 的返回值重新复制给 cars。现在 cars 的容量翻倍,变为 6。上面的程序输出为:
503
+ 在上面的程序中,cars 的容量开始时为 3。在第 10 行我们追加了一个新的元素给 ` cars ` ,并将 ` append(cars, "Toyota") ` 的返回值重新复制给 cars。现在 cars 的容量翻倍,变为 6。上面的程序输出为:
504
504
505
505
``` golang
506
506
cars: [Ferrari Honda Ford ] has old length 3 and capacity 3
@@ -588,7 +588,7 @@ func main() {
588
588
}
589
589
```
590
590
591
- 在上面的程序中,第 17 行将切片中的每个元素的值减2 。在函数调用之后打印切片的的内容,发现切片内容发生了改变。你可以回想一下,这不同于一个数组,对函数内部的数组所做的更改在函数外不可见。上面的程序输出如下:
591
+ 在上面的程序中,第 17 行将切片中的每个元素的值减 2 。在函数调用之后打印切片的的内容,发现切片内容发生了改变。你可以回想一下,这不同于一个数组,对函数内部的数组所做的更改在函数外不可见。上面的程序输出如下:
592
592
593
593
``` golang
594
594
array before function call [8 7 6 ]
@@ -634,7 +634,7 @@ Go Rust
634
634
635
635
切片保留对底层数组的引用。只要切片存在于内存中,数组就不能被垃圾回收。这在内存管理方便可能是值得关注的。假设我们有一个非常大的数组,而我们只需要处理它的一小部分,为此我们创建这个数组的一个切片,并处理这个切片。这里要注意的事情是,数组仍然存在于内存中,因为切片正在引用它。
636
636
637
- 解决该问题的一个方法是使用 copy 函数 ` func copy(dst, src []T) int ` 来创建该切片的一个拷贝。这样我们就可以使用这个新的切片,原来的数组可以被垃圾回收。
637
+ 解决该问题的一个方法是使用 copy 函数 ` func copy(dst, src []T) int ` 来创建该切片的一个拷贝。这样我们就可以使用这个新的切片,原来的数组可以被垃圾回收。
638
638
639
639
``` golang
640
640
package main
@@ -656,7 +656,7 @@ func main() {
656
656
}
657
657
```
658
658
659
- 在上面程序中,第 9 行 ` neededCountries := countries[:len(countries)-2] ` 创建一个底层数组为 ` countries ` 并排除最后两个元素的切片。第 11 行将 ` neededCountries ` 拷贝到 ` countriesCpy ` 并在下一行返回 ` countriesCpy ` 。现在数组 ` countries ` 可以被垃圾回收,因为 ` neededCountries ` 不再被引用。
659
+ 在上面程序中,第 9 行 ` neededCountries := countries[:len(countries)-2] ` 创建一个底层数组为 ` countries ` 并排除最后两个元素的切片。第 11 行将 ` neededCountries ` 拷贝到 ` countriesCpy ` 并在下一行返回 ` countriesCpy ` 。现在数组 ` countries ` 可以被垃圾回收,因为 ` neededCountries ` 不再被引用。
660
660
661
661
我(原文作者)已经将我们讨论的所有概念汇总到一个程序中,你可以从 [ github] ( https://github.com/golangbot/arraysandslices ) 下载。
662
662
@@ -666,9 +666,9 @@ func main() {
666
666
[ 深入解析 Go 中 Slice 底层实现] ( https://halfrost.com/go_slice/ )
667
667
###### 创建(定义)数组
668
668
数组在Go中是值类型,而不是引用(其他语言的数组则是引用类型)
669
- PS:切片(slice)是一个引用类型
670
- 数组不是统一的类型,大小不同的数组是不可以比较的
671
- 不同数组类型是不可以比较的
669
+
670
+ PS:切片(slice)是一个引用类型。 数组不是统一的类型,大小不同的数组是不可以比较的,不同数组类型是不可以比较的
671
+
672
672
``` golang
673
673
var a [2 ]int
674
674
var b [3 ]string
@@ -689,9 +689,9 @@ var d = [...]int{19:90} // 尽可能的满足索引值得数组
689
689
a := [...]int {19 :100 }
690
690
691
691
// 指向数组的指针
692
- var p *[20 ]int = &a // 长度为20的int型数组,这里的数组长度`20`必须和a数组长度相等
692
+ var p *[20 ]int = &a // 长度为20的int型数组,这里的数组长度 `20` 必须和a数组长度相等
693
693
fmt.Println (p) // 以上表示取这样一个数组的地址
694
- // 打印结果:&[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 19 ]
694
+ // 打印结果:&[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 100 ]
695
695
696
696
// 指向指针的数组
697
697
x , y := 1 , 2
@@ -702,7 +702,7 @@ fmt.Println("pp is ",pp) // 打印结果:pp is [0xc420012128 0xc420012130]
702
702
703
703
``` golang
704
704
ppp := new ([10 ]int )
705
- fmt.Println (" ----ppp-------" ,ppp); // 输出结果:`&[0 0 0 0 0 0 0 0 0 0]`
705
+ fmt.Println (" ----ppp-------" ,ppp); // 输出结果:`----ppp------- &[0 0 0 0 0 0 0 0 0 0]`
706
706
// 以上为指向数组的指针
707
707
```
708
708
@@ -712,12 +712,12 @@ fmt.Println("----ppp-------",ppp); //输出结果:`&[0 0 0 0 0 0 0 0 0 0]`
712
712
// 第一种方式
713
713
n := [10 ]int {}
714
714
n[1 ] = 10
715
- fmt.Println (" -----1------" ,n) // 输出:`[0 10 0 0 0 0 0 0 0 0]`
715
+ fmt.Println (" -----1------" ,n) // 输出:`-----1------ [0 10 0 0 0 0 0 0 0 0]`
716
716
717
717
// 第二种方式
718
718
m := new ([10 ]int )
719
719
m[1 ] = 20
720
- fmt.Println (" -----2------" ,m) // 输出:`&[0 20 0 0 0 0 0 0 0 0]`
720
+ fmt.Println (" -----2------" ,m) // 输出:`-----2------ &[0 20 0 0 0 0 0 0 0 0]`
721
721
```
722
722
723
723
###### 多维数组
@@ -804,7 +804,7 @@ func main() {
804
804
s7 := []byte {' a' ,' b' ,' c' ,' d' ,' e' ,' f' ,' g' ,' h' ,' i' ,' j' ,' k' } // 切片底层对应的数组
805
805
806
806
slice_a := s7[2 :5 ]
807
- fmt.Println (slice_a) // 输出的assica码 值 [99 100 101]
807
+ fmt.Println (slice_a) // 输出的ascii码 值 [99 100 101]
808
808
fmt.Println (string (slice_a)) // 格式化为字符串输出
809
809
fmt.Println (len (slice_a),cap (slice_a))
810
810
@@ -817,7 +817,7 @@ func main() {
817
817
s8 = append (s8, 12 , 48 )
818
818
fmt.Printf (" %v %p " , s8, s8) // 格式化打印值和内存地址:[0 0 0 12 48] 0xc042074030
819
819
820
- // 追加的元素如果没有超多切片容量 ,则切片的地址是不变的,否则内存地址会变
820
+ // 追加的元素如果没有超过切片容量 ,则切片的地址是不变的,否则内存地址会变
821
821
s8 = append (s8, 66 , 88 )
822
822
fmt.Printf (" %v %p \n " , s8, s8) // [0 0 0 12 48 66 88] 0xc042048060
823
823
@@ -828,7 +828,7 @@ func main() {
828
828
fmt.Println (s9) // [33 44 55 4 5 6 7]
829
829
830
830
copy (s10,s9)
831
- fmt.Println (s10) // [33 44 55 ]
831
+ fmt.Println (s10) // [1 2 3 ]
832
832
833
833
copy (s9[2 :4 ],s10[0 :2 ])
834
834
fmt.Println (s9) // [1 2 33 44 5 6 7]
0 commit comments