数组是具有相同唯一类型的一组已编号且长度固定的数据项序列(这是一种同构的数据结构);这种类型可以是任意的原始类型例如整形、字符串或者自定义类型(结构体、接口)。数组长度必须是一个常量表达式,并且必须是一个非负整数,数组下标是从0开始的,并且数组的长度是固定不可变的,数组中元素的地址是连续的。数组长度也是数组类型的一部分,所以[5]int和[10]int是属于不同类型的。
在其它编程语言中,数组一般都是引用类型,而在Go语言中,数组属于值类型。
注意:如果我们想让数组元素类型为任意类型的话可以使用空接口作为类型。当使用值时我们必须先做一个类型判断。
声明数组
Go语言数组声明需要指定元素类型及元素个数,语法格式如下:
// 变量名 [大小]类型
var variable_name [len]variable_type
示例:
定义一个长度为5的数组arr
var arr [5]int
以上我们声明了一个数组arr
,但是我们还没有对他进行初始化,但是声明的变量都会有默认值,int类型的变量默认值是0,所以arr数组中的每个元素的值都是0。
Go语言中的数组是一种值类型,所以可以通过 new() 来创建: var arr1 = new([5]int)
。那么这种方式和 var arr2 [5]int
的区别是什么呢?arr1 的类型是 *[5]int
,而 arr2的类型是 [5]int
。
数组在内存的结构
假如我们声明一个长度为5的数组var arr [5]int
,我们来看看这个数组在内存中的结构是怎么样的。
上图就是一个Go语言数组在内存的结构,在这个arr数组中,由于我们声明的是int32类型,所以数组中的每个元素在内存中占用的字节数是32/8=4字节,通过arr数组每个元素16进制地址我们可以发现,数组中后一个元素的地址比前一个元素的地址大4个字节,这个4字节正好是int32类型元素占用的内存大小,由此我们也能得出结论:数组中元素的内存地址是连续的;当声明数组时所有的元素都会被自动初始化为数组类型的默认值(int类型的默认值是0,所以arr中每个元素的默认值都是0);数组的下标识从0开始的。数组arr的地址就是数组第一个元素(下标为0的元素)的地址,通过下图打印arr的地址和arr[0]的地址就可以证明
初始化数组
刚刚声明的数组已经被默认的元素类型零值初始化了,如果我们再次进行初始化怎么做呢,可以采用如下办法:
var arr [5]int
arr = [5]int{1, 2, 3, 4, 5}
Go语言提供了声明加初始化的:=
操作符,以让我们在创建数组的时候直接初始化。
arr := [5]int{1, 2, 3, 4, 5}
这种简短变量声明的方式不仅适用于数组,还适用于任何数据类型,这也是Go语言中常用的方式。
我们也可以在定义数组是不明确指定长度,让编译器自动推导出长度,可以使用...
来替代具体的长度
arr := [...]int{1, 2, 3, 4, 5}
如果我们只想给数组的部分元素指定值,其他元素采用默认值我们可以采用下面的办法
arr := [5]int{0:3, 4:6}
这种方式表示我们只给数组下标为0的元素赋值为3,下标为4的元素赋值为6,其它元素的值依然是该类型的默认值(int的默认值是0)。
使用数组
数组元素的访问非常简单,通过索引(下标)即可访问数组的元素。
arr := [5]int{1, 2, 3, 4, 5}
fmt.Println(arr[0], arr[1], arr[2], arr[3], arr[4])
修改数组的某个元素也很简单:
arr := [5]int{1, 2, 3, 4, 5}
fmt.Println(arr[0])
arr[0] = 66
fmt.Println(arr[0])
如果我们要循环打印数组中的所有值,一个传统的就是常用的for循环:
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
不过大部分时候,我们都是使用for rang循环:
for i, v := range arr {
fmt.Println("索引是:", i, "值是:", v)
}
同样类型的数组是可以相互赋值的,不同类型的不行,会编译错误。那么什么是同样类型的数组呢?Go语言规定,必须是长度一样,并且每个元素的类型也一样的数组,才是同样类型的数组。
arr := [5]int{1, 2, 3, 4, 5}
var arr2 [5]int = arr //可以
var arr3 [3]int = arr //不可以
指针数组和数组本身差不多,只不过元素类型是指针。
arr := [5]*int{1: new(int), 3:new(int)}
这样就创建了一个指针数组,并且为索引1和3都创建了内存空间,其他索引是指针的零值nil
,这时候我们要修改指针变量的值也很简单,如下即可:
array := [5]*int{1: new(int), 3:new(int)}
*array[1] = 1
以上需要注意的是,只可以给索引1和3赋值,因为只有它们分配了内存,才可以赋值,如果我们给索引0赋值,运行的时候,会提示无效内存或者是一个nil指针引用。
panic: runtime error: invalid memory address or nil pointer dereference
要解决这个问题,我们要先给索引0分配内存,然后再进行赋值修改。
arr := [5]*int{1: new(int), 3:new(int)}
arr[0] =new(int)
*arr[0] = 2
fmt.Println(*arr[0])
函数间传递数组
在Go中,数组也是值类型,所以在函数间传递变量时,么就会整个复制,并传递给函数,如果数组非常大,比如长度100多万,那么这对内存是一个很大的开销。
func main() {
array := [5]int{1: 2, 3:4} //[0 2 0 4 0]
modify(array)
fmt.Println(array) //[0 2 0 4 0]
}
func modify(a [5]int){
a[1] = 3
}
通过上面的例子,可以看到,数组是复制的,原来的数组没有修改。我们这里是5个长度的数组还好,如果有几百万怎么办,有一种办法是传递数组的指针,这样,复制的大小只是一个数组类型的指针大小。
func main() {
array := [5]int{1: 2, 3:4} //[0 2 0 4 0]
modify(&array)
fmt.Println(array) //[0 333 0 4 0]
}
func modify(a *[5]int){
a[1] = 333
}
这里注意,数组的指针和指针数组是两个概念,数组的指针是
*[5]int
,指针数组是[5]*int
,注意*
的位置。
数组使用注意事项
数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化。
数组中的元素可以是任何数据类型,包括值类型和引用类型,但是不能混用。
-
数组创建后,如果没有赋值,有默认值
数值类型数组:默认值为0
字符串类型数组:默认值为""
bool数组: 默认值为false
指针数组: 默认值nil 使用数组的步骤:1.声明数组并开辟空间2.给数组各个元素赋值3.使用数组
数组的下标从0开始
数组下标必须在指定范围内使用,否则报panic:数组越界
Go的数组属于值类型 ,在默认情况下是值传递,因此会进行值拷贝。数组间不会相互影响
如果想在其它函数中,去修改原来的数组,可以使用引用传递(指针方式)
长度是数组类型的一部分,在传递函数参数时,需要考虑数组的长度