[golang]语法基础之数组

说明

在开发当中,经常需要存储集合数据。在go语言当中,一共有三种数据结构可以让用户管理集合数据,分别是数组、切片和映射。

相对来说,数组是其他两种的基础。

内部实现

在go语言当中,数组是一个长度固定的数据类型,用于存储一段具有相同的类型的元素的连续块。数组当中存储的数据类型可以是内置类型,例如整型、字符串等等,也可以是某种结构类型。因为是连续的,所以索引比较好计算,所以我们可以很快的索引数组中的任何数据。

灰色格子代表数组里的元素,每个元素都紧邻另一个元素。每个元素包含相同的类型,这个例子里是整数,并且每个元素可以用一个唯一的索引(也称下标或标号)来访问。

数组的声明和初始化

数组的声明以及初始化和其他类型差不多。声明的原则是:

  • 指明存储数据的类型。
  • 存储元素的数量,也就是数组的长度。

例如:

var array [5]int

上面的代码中我们声明了一个数组,长度为5,数组当中存储的数据类型为int。
此时虽然我们声明了数组,但是我们并没有对这个数组进行初始化,所有此时数组当中的值是对应数据类型的零值。也就是说此时数组的元素是5个0。

相对于其他的一些语言来说,在go当中,数组一旦声明,就不能改变其类型和长度。那么此时如果想要在数组中存储更多的数据,就只能通过创建一个新的数组,然后把原本数组里面的值复制过去。

上面的代码中我们已经对数组进行了声明,那么我们该如何进行初始化操作呢?

如下:

var array [5]int 
array = [5]int{1,2,3,4,5}

如果你觉得上面的这种写法比较繁琐,还可以使用简短的初始化方式,就是在数组创建的时候通过:= 直接进行初始化。

array := [5]int{1,2,3,4,5}

上面这种简短的声明方式不仅仅适用于数组类型,其他的数据类型也可以使用这种方式进行声明和初始化。

如果在创建和初始化数组的时候不想指定数组的长度怎么办? 可以采用下面的这种形式:

arr := [...]int{1,2,3,4}

在上面的代码中,我们通过...代替了数组的长度,go会自动推导出数组的长度。

那么如果我们只想给索引为1和2的数组初始化相应的值,其他位置的元素都为零值,那么该怎么办呢?如下:

arr := [...]int{0,3,4,0,0} 

在上面的代码中,除了1和2的位置上的元素进行了初始化,其他位置上的元素都设置了零值。但是上面的这种方法看上去容易让人产生误解,还可以采用下面这种更好的方法:

arr := [5]int{1:3,2:4}

在上面的代码中,我们创建了一个数组,索引值为1 和 2 的位置上的元素的值为3 和 4 ,分别进行了初始化,但是其他位置上的元素我们利用了数组的特性,没有初始化的值都为该类型的零值。

使用数组

go语言当中的数组访问和其他语言差不多,都是通过索引值来进行访问数组的元素,操作符为[]。因为数组的内存是连续的,所以通过索引访问的效率非常高。

例如:

array := [3]string{"hello","world","!"}
fmt.Println(array[1]) // world

修改数组的元素和访问类似,通过索引值找到数组元素,然后直接进行更改就好。

如下:

arr := [3]string{"hello","world","!"}
arr[2] = "?"
fmt.Println(arr) // hello world ?

如果我们需要遍历数组,可以采用循环的形式。

// 采用 传统的for循环
func main() {
    array := [5]int{1: 1, 3: 4}

    for i := 0; i < 5; i++ {
        fmt.Printf("索引:%d,值:%d\n", i, array[i])
    }
}

// 也可以采用for range循环
func main() {
    array := [5]int{1: 1, 3: 4}

    for i, v := range array {
        fmt.Printf("索引:%d,值:%d\n", i, v)
    }
}

在go语言当中,相同类型的数组可以进行相互赋值,不同类型的数组不行,会产生编译错误。

那么在go语言当中什么是相同类型的数组呢?

go语言规定,首先必须是长度相同,并且每个元素的类型也必须一致。这样才算是相同类型的数组。

例如:

arr := [5]int{1,2,3,4,5}
var arr2 [5]int = arr // 长度和类型都相同,可以赋值成功
var arr3 [4]int = arr // 长度不相同,赋值失败

指针数组和普通的数组差不多,只不过元素类型是指针。

arr := [4]*int{1:new(int),3:new(int)}

上面这段代码创建了一个指针数组,并且为索引为1和3的元素创建了内存空间,其他元素的索引是指针的零值nil。这时我们改变指针变量的值也很简单。如下:

arr := [4]*int{1:new(int),3:new(int)}
*arr[1] = 10 

上面的代码需要注意,我们只能够给索引值1 和 3 的位置进行赋值,原因是我们只给它们分配了内存,可以赋值,如果我们给其他没有创建内存空间的位置上进行赋值,那么运行的时候,会提示无效内存或者是一个nil指针引用。

panic: runtime error: invalid memory address or nil pointer dereference

如果想要给其他位置的索引进行赋值,必须要先通过new进行分配内存,才能够进行赋值。

函数间传递数组

在函数间传递变量时,总是以值得形式进行传递,如果传递的值是一个数组,就会把这个数组进行整个复制,并且传递给函数。如果数组非常大,那么对内存就是一个非常大的开销。

func main() {
    array := [5]int{1: 2, 3:4}
    modify(array)
    fmt.Println(array)
}

func modify(a [5]int){
    a[1] =3
    fmt.Println(a)
}

通过上面的示例,可以看到,数组传递到函数里面是值复制的,原来的数组并没有发生修改。如果数组的值比较多怎么办?我们可以通过传递数组的指针,这样,复制的大小就只是一个数组类型的指针大小。

func main() {
    array := [5]int{1: 2, 3:4}
    modify(&array)
    fmt.Println(array)
}

func modify(a *[5]int){
    a[1] =3
    fmt.Println(*a)
}

在上面的代码中,我们将一个数组的指针传递到了函数当中,这样在函数中修改数组,原数组也就会发生改变。所以这种情况虽然节省了复制的内存,但是要谨慎使用,因为一不小心,就会修改原数组,导致不必要的问题。

这里注意,数组的指针和指针数组是两个概念,数组的指针是[5]int,指针数组是[5]int,注意*的位置。

针对函数间传递数组的问题,比如复制问题,比如大小僵化问题,都有更好的解决办法,这个就是切片,相对于数组来说会更加的灵活。

猜你喜欢

转载自www.cnblogs.com/liujunhang/p/12534583.html
今日推荐