了解Python、Perl、JavaScript的人想必都知道它们的数组是动态的,能够随需求自动增大数组长度。但Go中的数组是固定长度的,数组一经声明,就没法扩大、缩减数组的长度。但Go中也有相似的动态"数组",称为slice数据结构,在下一篇文章会详细解释它。数组
Go中的数组是slice和map两种数据类型的基础,这两种数据类型的底层都是经过数组实现的。缓存
当在Go中声明一个数组以后,会在内存中开辟一段固定长度的、连续的空间存放数组中的各个元素,这些元素的数据类型彻底相同,能够是内置的简单数据类型(int、string等),也能够是自定义的struct类型。数据结构
例如,使用下面的语句声明一个长度为4的int类型的数组,那么这个数组最多只能存放4个元素,且全部元素都只能是int类型。同时,还为这个数组作了初始化。函数
arr_name := [4]int{3,5,22,12}
这个数组的结构以下图所示:指针
其中左上角的小格子中的数表示各元素所在数组中的位置,也就是它们对应的index,index从0开始计算。code
由于Go中的数组要求数据类型固定、长度固定,因此在声明的时候须要给定长度和数据类型。对象
例如声明一个长度为五、数据类型为int的数组,名为arr_name。blog
var arr_name [5]int
必须注意,虽然咱们称呼数组为int类型的数组,但数组的数据类型是两部分组成的[n]TYPE
,这个总体才是数组的数据类型。因此,[5]int
和[6]int
是两种不一样的数组类型。不一样数据类型,意味着若是数组赋值给另外一数组时须要数据类型转换操做,而Go默认是不会进行数据类型转换的。索引
在Go中,当一个变量被声明以后,都会当即对其进行默认的赋0初始化。对int类型的变量会默认初始化为0,对string类型的变量会初始化为空"",对布尔类型的变量会初始化为false,对指针(引用)类型的变量会初始化为nil。ip
数组也是一种变量类型,也会被初始化。初始化的方式是数组中的全部元素都根据数据类型赋值0。例如int类型的数组,元素所有赋值为0,string类型的数组,元素所有赋值为""等。
因此,上面声明数组arr_name以后,它初始化后的结果以下:
能够直接输出数组:
import "fmt" var new_arr [3]int fmt.Println(new_arr) // 输出:[0 0 0]
能够将数组的声明和初始化为给定值的操做合并:
arr_name := [5]int{3,5,22,12,23}
若是将元素个数指定为特殊符号...
,则表示经过初始化时的给定的值个数来推断数组长度:
// 声明长度为3的数组 arr_name1 := [...]int{2,3,4} // 声明长度为4的数组 arr_name2 := [...]int{2,3,4,5}
若是声明数组时,只想给其中某几个元素初始化赋值,则使用索引号:
arr_name := [5]int{1:10, 2:20}
这表示声明长度为5的数组,但第2个元素的值为10,第3个元素的值为20,其它的元素(第一、四、5个元素)都默认初始化为0。
这个数组声明后的结果以下:
要访问数组中的某个元素,可使用索引:
arr_name := [5]int{2,3,4,5,6} // 访问数组的第4个元素,将输出:5 print(arr_name[3]) // 修改数组第3个元素的值 arr_name[2] = 22
能够声明一个指针类型的数组,这样数组中就能够存放指针。注意,指针的默认初始化值为nil。
例如,建立一个指向int类型的数组:
arr_name := [5]*int{1:new(int), 3:new(int)}
上面的*int
表示数组只能存储*int
类型的数据,也就是指向int的指针类型。new(TYPE)
函数会为一个TYPE类型的数据结构划份内存并作默认初始化操做,并返回这个数据对象的指针,因此new(int)表示建立一个int类型的数据对象,同时返回指向这个对象的指针。
初始化后,它的结构以下:注意int指针指向的数据对象会被初始化为0。
对数组中的指针元素进行赋值:
package main import "fmt" func main() { arr_name := [5]*int{1:new(int), 3:new(int)} *arr_name[1]=10 *arr_name[3]=30 // 赋值一个新元素 arr_name[4]=new(int) fmt.Println(*arr_name[1]) fmt.Println(*arr_name[3]) fmt.Println(*arr_name[4]) }
在Go中,因为数组算是一个值类型,因此能够将它赋值给其它数组。
由于数组类型的完整定义为[n]TYPE
,因此数组赋值给其它数组的时候,n和TYPE必须相同。
例如:
// 声明一个长度为5的string数组 var str_arr1 [5]string // 声明并初始化另外一个string数组 str_arr2 := [5]string{"Perl","Shell","Python","Go","Java"} // 将str_arr2拷贝给str_arr1 str_arr1 = str_arr2
数组赋值给其它数组时,其实是完整地拷贝一个数组。因此,若是数组是一个指针型的数组,那么拷贝的将是指针数组,而不会拷贝指针所指向的对象。
package main import "fmt" func main() { var str_arr1 [3]*string str_arr2 := [3]*string{ new(string), new(string), new(string), } *str_arr2[0] = "Perl" *str_arr2[1] = "Python" *str_arr2[2] = "Shell" // 数组赋值,拷贝指针自己,而不拷贝指向的值 str_arr1 = str_arr2 // 将输出Python fmt.Println(*str_arr1[1]) }
拷贝后,它的结构以下:
range关键字能够对array进行迭代,每次返回一个index和对应的元素值。能够将range的迭代结合for循环对array进行遍历。
package main func main() { my_arr := [4]int{11,22,33,44} for index,value := range my_arr { println("index:",index," , ","value",value) } }
输出结果:
index: 0 , value 11 index: 1 , value 22 index: 2 , value 33 index: 3 , value 44
Go中的传值方式是按值传递,这意味着给变量赋值、给函数传参时,都是直接拷贝一个副本而后将副本赋值给对方的。这样的拷贝方式意味着:
数组一样也遵循此规则。对于数组的赋值,上面数组拷贝中已经解释过了。若是函数的参数是数组类型,那么调用函数时传递给函数的数组也同样是这个数组拷贝后的一个副本。
例如,建立一个100W个元素的数组,将其传递给函数foo():
var big_arr [1e6]int func foo(a [1e6]int) { ... } // 调用foo foo(bigarr)
当上面声明big_arr后,就有100W个元素,假设这个int占用8字节,整个数组就占用800W字节,大约有8M数据。当调用foo的时候,Go会直接复制这8M数据造成另外一个数组副本,并将这个副本交给foo进行处理。在foo中处理的数组,其实是这个副本,foo()不会对原始的big_arr产生任何影响。
能够将数组的指针传递给函数,这样指针传递给函数时,复制给函数参数的是这个指针,总共才8个字节(每一个指针占用1个机器字长,64位机器上是64bit共占用8字节),复制的数据量很是少。并且,由于复制的是指针,foo()修改这个数组时,会直接影响原始数组。
var big_arr [1e6]int // 生成数组的指针 ref_big_arr := &big_arr func foo(ra *[1e6]int) { ... } // 调用foo,传递指针 foo(ref_big_arr)
能够经过组合两个一维数组的方式构成二维数组。通常在处理具备父、子关系或者有坐标关系的数据时,二维数组比较有用。
例如,声明二维数组:
var t_arr [4][2]int
这表示数组有4个元素,每一个元素都是一个包含2元素的小数组。换一种方式,例如:
t_arr := [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
还能够指定位置进行初始化:
t_arr := [4][2]int{1: {20, 21}, 3: {40, 41}} t_arr := [4][2]int{1: {0: 20}, 3: {1: 41}}