1. 数组: 是同一种数据类型的固定长度的序列.
2. 数组定义: var a [len]int, 比如: var a [5]int, 数组长度必须是常量, 且是类型的组成部分. 一旦定义, 长度不能变.
3. 长度是数组类型的一部分, 因此, var a[5] int 和 var a[10]int 是不同的类型.
4. 数组可以通过下标进行访问, 下标是从 0 开始, 最后一个元素下标是: len-1
- for i := 0; i < len(a); i++ {
- }
- for index, v := range a {
- }
5. 访问越界, 如果下标在数组合法范围之外, 则触发访问越界, 会 panic
6. 数组是值类型, 赋值和传参会复制整个数组, 而不是指针. 因此改变副本的值, 不会改变本身的值.
7. 支持 "==","!=" 操作符, 因为内存总是被初始化过的.
8. 指针数组 [n]*T, 数组指针 *[n]T.
数组介绍
数组是一个由固定长度的特定类型元素组成的序列, 一个数组可以由零个或多个元素组成. 数组的长度是数组类型的组成部分.
因为数组的长度是数组类型的一个部分, 不同长度或不同类型的数据组成的数组都是不同的类型, 因此在 Go 语言中很少直接使用数组 (不同长度的数组因为类型不同无法直接赋值).
和数组对应的类型是切片, 切片是可以动态增长和收缩的序列, 切片的功能也更加灵活, 但是要理解切片的工作原理还是要先理解数组.
数组的内存结构:
一维数组初始化
- var a [3]int // 定义长度为 3 的 int 型数组, 元素全部为 0
- var b = [...]int{
- 1, 2, 3
- } // 定义长度为 3 的 int 型数组, 元素为 1, 2, 3
- var c = [...]int{
- 2: 3, 1: 2
- } // 定义长度为 3 的 int 型数组, 元素为 0, 2, 3
- var d = [...]int{
- 1, 2, 4: 5, 6
- } // 定义长度为 6 的 int 型数组, 元素为 1, 2, 0, 0, 5, 6
第一种方式是定义一个数组变量的最基本的方式, 数组的长度明确指定, 数组中的每个元素都以零值初始化.
第二种方式定义数组, 可以在定义的时候顺序指定全部元素的初始化值, 数组的长度根据初始化元素的数目自动计算.
第三种方式是以索引的方式来初始化数组的元素, 因此元素的初始化值出现顺序比较随意. 这种初始化方式和 map[int]Type 类型的初始化语法类似. 数组的长度以出现的最大的索引为准, 没有明确初始化的元素依然用 0 值初始化.
第四种方式是混合了第二种和第三种的初始化方式, 前面两个元素采用顺序初始化, 第三第四个元素零值初始化, 第五个元素通过索引初始化, 最后一个元素跟在前面的第五个元素之后采用顺序初始化.
二维数组初始化
全局
- var arr0 [5][3]int
- var arr1 [2][3]int = [...][3]int{
- {
- 1, 2, 3
- }, {
- 7, 8, 9
- }
- }
局部:
- a := [2][3]int{
- {
- 1, 2, 3
- }, {
- 4, 5, 6
- }
- }
- b := [...][2]int{
- {
- 1, 1
- }, {
- 2, 2
- }, {
- 3, 3
- }
- } // 第 2 纬度不能用 "...".
代码:
- package main
- import (
- "fmt"
- )
- var arr0 [5][3]int
- var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
- func main() {
- a := [2][3]int{{1, 2, 3}, {4, 5, 6}}
- b := [...][2]int{{1, 1}, {2, 2}, {3, 3}} // 第 2 纬度不能用 "...".
- fmt.Println(arr0, arr1)
- fmt.Println(a, b)
- }
输出结果:
- [[0 0 0] [0 0 0] [0 0 0] [0 0 0] [0 0 0]] [[1 2 3] [7 8 9]]
- [[1 2 3] [4 5 6]] [[1 1] [2 2] [3 3]]
内置函数 len 和 cap 都返回数组长度 (元素数量)
- package main
- func main() {
- a := [2]int{}
- println(len(a), cap(a))
- }
输出结果:
2 2
数组布局
Go 语言中数组是值语义 (所谓值语义是一个对象被系统标准的复制方式复制后, 与被复制的对象之间毫无关系, 可以彼此独立改变互不影响). 一个数组变量即表示整个数组, 它并不是隐式的指向第一个元素的指针 (比如 C 语言的数组), 而是一个完整的值. 当一个数组变量被赋值或者被传递的时候, 实际上会复制整个数组. 如果数组较大的话, 数组的赋值也会有较大的开销. 为了避免复制数组带来的开销, 可以传递一个指向数组的指针, 但是数组指针并不是数组.
- var a = [...]int{1, 2, 3} // a 是一个数组
- var b = &a // b 是指向数组的指针
- fmt.Println(a[0], a[1]) // 打印数组的前 2 个元素
- fmt.Println(b[0], b[1]) // 通过数组指针访问数组元素的方式和数组类似
- for i, v := range b { // 通过数组指针迭代数组的元素
- fmt.Println(i, v)
- }
其中 b 是指向 a 数组的指针, 但是通过 b 访问数组中元素的写法和 a 类似的. 还可以通过 for range 来迭代数组指针指向的数组元素. 其实数组指针类型除了类型和数组不同之外, 通过数组指针操作数组的方式和通过数组本身的操作类似, 而且数组指针赋值时只会拷贝一个指针. 但是数组指针类型依然不够灵活, 因为数组的长度是数组类型的组成部分, 指向不同长度数组的数组指针类型也是完全不同的.
可以将数组看作一个特殊的结构体, 结构的字段名对应数组的索引, 同时结构体成员的数目是固定的. 内置函数 len 可以用于计算数组的长度, cap 函数可以用于计算数组的容量. 不过对于数组类型来说, len 和 cap 函数返回的结果始终是一样的, 都是对应数组类型的长度.
遍历数组
我们可以用 for 循环来迭代数组. 下面常见的几种方式都可以用来遍历数组:
- for i := range a {
- fmt.Printf("a[%d]: %d\n", i, a[i])
- }
- for i, v := range b {
- fmt.Printf("b[%d]: %d\n", i, v)
- }
- for i := 0; i < len(c); i++ {
- fmt.Printf("c[%d]: %d\n", i, c[i])
- }
用 for range 方式迭代的性能可能会更好一些, 因为这种迭代可以保证不会出现数组越界的情形, 每轮迭代对数组元素的访问时可以省去对下标越界的判断.
用 for range 方式迭代, 还可以忽略迭代时的下标:
- var times [5][0]int
- for range times {
- fmt.Println("hello")
- }
- package main
- import (
- "fmt"
- )
- func main() {
- var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
- for k1, v1 := range f {
- for k2, v2 := range v1 {
- fmt.Printf("(%d,%d)=%d", k1, k2, v2)
- }
- fmt.Println()
- }
- }
- (0,0)=1 (0,1)=2 (0,2)=3
- (1,0)=7 (1,1)=8 (1,2)=9
- // 字符串数组
- var s1 = [2]string{"hello", "world"}
- var s2 = [...]string{"你好", "世界"}
- var s3 = [...]string{1: "世界", 0: "你好", }
- // 结构体数组
- var line1 [2]image.Point
- var line2 = [...]image.Point{image.Point{X: 0, Y: 0}, image.Point{X: 1, Y: 1}}
- var line3 = [...]image.Point{{0, 0}, {1, 1}}
- // 图像解码器数组
- var decoder1 [2]func(io.Reader) (image.Image, error)
- var decoder2 = [...]func(io.Reader) (image.Image, error){
- PNG.Decode,
- jpeg.Decode,
- }
- // 接口数组
- var unknown1 [2]interface{}
- var unknown2 = [...]interface{}{123, "你好"}
- // 管道数组
- var chanList = [2]chan int{}
- var d [0]int // 定义一个长度为 0 的数组
- var e = [0]int{
- } // 定义一个长度为 0 的数组
- var f = [...]int{
- } // 定义一个长度为 0 的数组
- c1 := make(chan [0]int)
- go func() {
- fmt.Println("c1")
- c1 <- [0]int{}
- }()
- <-c1
- c2 := make(chan struct{})
- go func() {
- fmt.Println("c2")
- c2 <- struct{}{} // struct{} 部分是类型, {} 表示对应的结构体值
- }()
- <-c2
- fmt.Printf("b: %T\n", b) // b: [3]int
- fmt.Printf("b: %#v\n", b) // b: [3]int{1, 2, 3}
- https://chai2010.cn/advanced-go-programming-book/ch1-basic/ch1-03-array-string-and-slice.html
- https://www.kancloud.cn/liupengjie/go/574250
来源: http://www.bubuko.com/infodetail-3100935.html