08 Go语言数组

数组是由相同数据类型的元素组成的一组已编号且长度固定的数据项序列。

数组的特点:

  • 每个元素具有相同的数据类型
  • 每个元素在数组中有唯一的编号,称作数组下标,下标从0开始
  • 数组的长度是固定的,而且该长度必须是一个常量表达式

由于数组的长度是固定的,而切片类型的长度可以动态变化,因此在Go里面很少直接使用数组而是使用切片。但是为了更好的理解切片,我们必须先理解数组。


8.1 数组的声明和初始化

数组的声明

Go语言声明数组时需要指明元素类型和元素个数,语法如下:

var variable_name [SIZE]variable_type

例如:

// 定义一个长度为10的int数组
var values [10]int

数组的初始化

以上声明的数组虽然没有明确的初始化,但编译器会使用零值对数组每个元素初始化。int类型的零值是数字0,因此上面的数组每个元素的值都是数字0。

当然,也可以在声明数组的时候对其明确的初始化,如下:

// 需注意:初始化时,{}中的元素个数不能超过[]中的数字。
var values = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 

也可以省略[]中的数字,Go语言会根据元素的个数自动设置数组的大小,如下:

// 本例子与上面的例子时一样的,虽然没有明确设置数组大小
var values = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

如果我们只想给数组的部分元素初始化,其他元素初始为零值我们可以用下面的方式初始化:

// 只给数组下标为0的元素赋值为5,下标为4的元素赋值为5,其他下标的元素初始化为零值
var values = [10]int{0:2, 4:5}

8.2 数组在内存中的结构

var values = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

上面代码所定义的数组在内存中的结构如下:

由上面的图我们可知:

  • 数组的起始地址就是它第一个元素的地址,即:&values == &values[0]。
  • 数组每个元素占用内存大小是相等的,对于int数组,每个元素都占8字节(在64位的系统上,int类型是8个字节)。
  • 数组的元素在内存中是连续分配的,对于int数组,每个元素的地址比前一个元素地址大8字节,这8字节正好是单个数组元素的大小。

8.3 使用数组

访问数组元素

数组元素可以通过索引(位置)来读取。如下:

var values = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println(values[0])
fmt.Println(values[1])
fmt.Println(values[2])
i := 3
fmt.Println(values[i])
i++
fmt.Println(values[i])

访问数组元素时不可以越界,否则发生宕机异常:

var values = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
v := values[10] // 数组下标最大值时9,使用下标10访问数组元素发生越界

修改元素值

var values = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
values[0] = 100
values[1] = 200
values[2] += 300
fmt.Println(values)

遍历数组

使用索引遍历:

for i := 0; i < len(values); i++ {	// len()返回数组元素个数
    fmt.Println(values[i])
}

使用for-range遍历:

for i, v := range values {
    fmt.Println("索引是:", i, "值是:", v)
}

相同类型的数组互相赋值

什么是相同类型的数组?Go语言规定,长度一样且元素类型一样的数组就是相同类型的数组。

values := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

var values2 [10]int = values	// 可以
var values3 [8]int = values		// 不可以

由于数组属于值类型,所以数组赋值的时候会拷贝整个数组。


向函数传递数组

如果你希望向函数传递数组,那么在定义函数的时候需要声明形参是数组:

func myFunction(param [10]int) {
   // ...
}

调用该函数时,传入一个数组,由于数组是值类型,因此给myFunction的数组是原来数组的拷贝,对形参数组的修改不会改变原来的数组。例如:

func main() {
	var values = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	myFunction(values)
	fmt.Println(values)  // 输出 [1 2 3 4 5 6 7 8 9 10]
}

func myFunction(param [10]int) {
    param[0] = 100
}

向函数传递数组的指针

如果想向函数传递数组的指针,那么在定义函数的时候需要声明形参是数组指针类型:

func myFunction(param *[10]int) {
    // ... 
}

对于这种方式,由于向函数传递的是数组的地址,因此在函数内部对数组内容修改后,修改的就是原数组。例如:

func main() {
	var values = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	myFunction(&values)
	fmt.Println(values)  // 输出 [100 2 3 4 5 6 7 8 9 10]
}

func myFunction(param *[10]int) {
    param[0] = 100	// Go语言会自动对指针解引用,本语句等价于 (*param)[0] = 1000
}

数组使用注意事项

  • 数组的元素都是同一类型的

  • 数组的长度是固定的,定义完数组之后,数组的长度不能动态变化

  • 数组声明时,如果未明确初始化,那么会把各元素初始化为元素类型对应的零值

  • 数组下标从0开始

  • 不可以越界访问数组,否则发生宕机异常

  • 数组属于值类型,默认情况下是值传递,因此会进行值拷贝

  • 如果想在其他函数中修改原来的数组,可以使用传数组指针的方式

  • 类型相同但长度不同的数组,不是同一类型的数组,不可以相互赋值


Copyright@2022 , 359152155@qq.com