数组是由相同类型元素的集合组成的数据结构,计算机会为数组分配一块连续的内存来保存其中的元素,我们可以利用数组中元素的索引快速访问特定元素,内置的len函数将返回数组中元素的个数。
var a [3]int // array of 3 integers
fmt.Println(a[0]) // print the first element
fmt.Println(a[len(a)-1]) // print the last element, a[2]
数组作为一种基本的数据类型,我们通常会从两个维度描述数组,也就是数组中存储的元素类型和数组最大能存储的元素个数,Go 语言数组在初始化之后大小就无法改变,存储元素类型相同、但是大小不同的数组类型在 Go 语言看来也是完全不同的,只有两个条件都相同才是同一类型。
func foo(arr [5]int) {}
func main() {
var arr1 [5]int
var arr2 [6]int
var arr3 [5]string
foo(arr1) // ok
foo(arr2) // 错误:[6]int与函数foo参数的类型[5]int不是同一数组类型
foo(arr3) // 错误:[5]string与函数foo参数的类型[5]int不是同一数组类型
}
数组初始化
Go 语言的数组有两种不同的创建方式,一种是显式的指定数组大小,另一种是使用 [...]T
声明数组,Go 语言会在编译期间通过源代码推导数组的大小:
arr1 := [3]int{1, 2, 3}
arr2 := [...]int{1, 2, 3}
和基本数据类型一样,我们声明一个数组类型变量的同时,也可以显式地对它进行初始化。如果不进行显式初始化,那么数组中的元素值就是它类型的零值。
var arr1 [6]int // [0 0 0 0 0 0]
访问和赋值
数组在内存中都是一连串的内存空间,我们通过指向数组开头的指针、元素的数量以及元素类型占的空间大小表示数组。如果我们不知道数组中元素的数量,访问时可能发生越界;而如果不知道数组中元素类型的大小,就没有办法知道应该一次取出多少字节的数据,无论丢失了哪个信息,我们都无法知道这片连续的内存空间到底存储了什么数据.
数组访问越界是非常严重的错误,Go 语言中可以在编译期间的静态类型检查判断数组越界。
数组的问题
数组类型变量是一个整体,这就意味着一个数组变量表示的是整个数组。当调用一个函数的时候,函数的每个调用参数将会被赋值给函数内部的参数变量,所以函数参数变量接收的是一个复制的副本,并不是原始调用的变量。Go 传递数组的方式都是纯粹的值拷贝,这会带来较大的内存拷贝开销,并且对数组参数的任何的修改都是发生在复制的数组上,并不能直接修改调用时原始的数组变量。
参考资料
- 《Go语言圣经》
- 《Go语言第一课》
- 《 Go语言设计与实现》