Go语言数组的用法(非常详细)
数组是指具有固定长度且元素类型相同的集合,它由元素和长度两个部分组成。元素可以是任意的类型,例如整形、字符串或者自定义类型。
数组的长度必须是常量,并且此长度是数组的一部分。一旦数组被定义好了,那么长度就不能再更改。
我们通过下标访问数组,下标表示的是偏移量,所以第一个元素是 [0],而不是 [1]。最后一个元素的下标是长度减 1,如果访问越界就会产生 panic。
数组是值类型,所以赋值和传参会复制整个数组。改变副本的值时,并不会改变数组本身的值。
初始化数组有如下几种方法:
示例代码如下:

图 1 数组在内存中的布局示意图
上述代码的输出信息可以说明三个事实:
第一,数组中的元素会被逐个存储。也就是说,它们会出现在连续的内存空间中。
第二,数组中每个元素地址之间的间隔是由元素的类型决定的,且每个元素和下一个元素之间的距离是可以预测的。在上例中,定义了 int 类型的数组,int 类型的长度为 8 字节,因此数组元素之间的地址间隔为 8。
第三,数组中第一个元素的地址是数组的地址。
示例代码如下:
需要注意的是,遍历时获得的元素是原数组值的副本,修改双方中的任何一方都不会影响对方。在以下代码中,修改了原数组 str 下标为 1 的元素,但下标为 1 的值是从原数组值的备份中获取的,因此输出的仍然是 b,而非修改值 Golang:
示例代码如下:
在 Go语言中对两数进行交换有一种特殊写法,即 arr[i], arr[j] = arr[j], arr[i]。因此,上面数组反转的代码片段可以简化为:
数组的长度必须是常量,并且此长度是数组的一部分。一旦数组被定义好了,那么长度就不能再更改。
我们通过下标访问数组,下标表示的是偏移量,所以第一个元素是 [0],而不是 [1]。最后一个元素的下标是长度减 1,如果访问越界就会产生 panic。
数组是值类型,所以赋值和传参会复制整个数组。改变副本的值时,并不会改变数组本身的值。
Go语言数组的声明及初始化
声明数组时,需要指定元素的类型和数组的长度。在使用 var 声明变量时,Go 程序总是会使用该类型变量的零值来初始化变量。初始化数组有如下几种方法:
1) 常规的初始化方法
常规的初始化方法的示例代码如下:var arr01 [3]int = [3]int{1, 2, 3} fmt.Println("arr01=", arr01) //输出: arr01= [1 2 3]
2)使用类型推导初始化
相较第一种,此方法省略了类型,编译器会根据初始化的值来推导变量的类型,示例代码如下:var arr02 = [3]int{5, 6, 7} fmt.Println("arr02=", arr02) //输出: arr02= [5 6 7]
3) 使用“[...]”语法糖初始化
“[...]”是一种语法糖,使用这种方式时可以不明确写出数组的长度,数组的长度会根据初始化的值推导得出,示例代码如下:var arr03 = [...]int{8, 9, 10} //这里的 [...] 是一种语法糖 fmt.Println("arr03=", arr03) //输出: arr03= [8 9 10]
4) 带索引的初始化方法
可以在初始化的每个值前加上索引,这样一来,数组在初始化时就会根据索引的顺序进行存储,且会按照存储的顺序输出,示例代码如下:var arr04 = [...]int{1: 800, 0: 900, 2: 999} fmt.Println("arr04=", arr04) //输出: arr04= [900 800 999]
5) 综合的初始化方法
这是一种综合了类型推导、“[...]”语法糖和索引的初始化方法,示例代码如下:arr05 := [...]string{1: "abc", 0: "Go语言", 2: "2020"} fmt.Println("arr05=", arr05) //输出: arr05= [Go语言 abc 2020]
数组在内存中的形式
既然对硬件来说,数组是最重要的数据结构,那让我们看看数组是如何存储在内存中的,是否与前面提到的可预测内存模式相匹配。示例代码如下:
//int占8字节 var intArr = [3]int{100, 200, 300} fmt.Printf("intArr的地址=%p\n", &intArr) //输出:intArr的地址=0xc0000b8000 fmt.Printf("intArr[0] 地址=%p\n", &intArr[0]) //输出:intArr[0] 地址=0xc0000b8000 fmt.Printf("intArr[1] 地址=%p\n", &intArr[1]) //输出:intArr[1] 地址=0xc0000b8008 fmt.Printf("intArr[2] 地址=%p\n", &intArr[2]) //输出:intArr[2] 地址=0xc0000b8010下图是根据上面的结果画出的数组在内存中的布局示意图:

图 1 数组在内存中的布局示意图
上述代码的输出信息可以说明三个事实:
第一,数组中的元素会被逐个存储。也就是说,它们会出现在连续的内存空间中。
第二,数组中每个元素地址之间的间隔是由元素的类型决定的,且每个元素和下一个元素之间的距离是可以预测的。在上例中,定义了 int 类型的数组,int 类型的长度为 8 字节,因此数组元素之间的地址间隔为 8。
第三,数组中第一个元素的地址是数组的地址。
Go语言数组的遍历
除了根据现有的长度遍历数组,还可以使用语法糖遍历数组:for index, value := range arrayindex 表示数组下标,value 表示下标对应的元素。它们都仅仅在 for 循环中可见,如果不想使用下标 index,可以使用下画线(_)屏蔽。
示例代码如下:
str := [...]string{"a", "b", "c"} for i, v := range str { fmt.Print(i,":",v,";") //输出:0:a;1:b;2:c; }
需要注意的是,遍历时获得的元素是原数组值的副本,修改双方中的任何一方都不会影响对方。在以下代码中,修改了原数组 str 下标为 1 的元素,但下标为 1 的值是从原数组值的备份中获取的,因此输出的仍然是 b,而非修改值 Golang:
str := [3]string{"a", "b", "c"} for i, v := range str { str[1] = "Golang" if i == 1 { fmt.Printf("v[%s]\n", v) //输出b,而非Golang } }
Go语言数组的截取
使用如“a[开始索引(包含), 结束索引(不包含)]”的形式可以对数组进行截取。截取时,对原数组采取的原则是左闭右开。示例代码如下:
a := [...]int{1, 2, 3, 4, 5} //截取时,左闭右开 a[1:2] //2 a[1:3] //2,3 a[1:len(a)] //2,3,4,5 a[1:] //2,3,4,5 a[:3] //1,2,3
Go语言数组的反转
在对数组进行反转操作时,可参考其他语言的做法,即利用临时变量来实现,示例代码如下:length := len(arr) for i := 0; i < length/2; i++ { temp := arr[length-1-i] arr[length-1-i] = arr[i] arr[i] = temp }
在 Go语言中对两数进行交换有一种特殊写法,即 arr[i], arr[j] = arr[j], arr[i]。因此,上面数组反转的代码片段可以简化为:
for i, j := 0, len(arr)-1; i < j; i, j = i+1, j-1 { arr[i], arr[j] = s[j], s[i] }