go的数组是用于存放同一类型数据的集合。
数组同样是一种类型,在go中,数组是值类型,赋值和传参会进行值复制。
一、定义数组
- var 数组名 [长度]类型
- var 数组名: [长度]类型 = [长度]类型{元素1,元素2,...}
- var 数组名: [长度]类型 = {元素1,元素2,...}
- var 数组名 = [长度]类型{元素1,元素2,...}
- var 数组名 = [长度]类型{下标: 元素, ...}
- 数组名 := [长度]类型{元素1,元素2,下标: 元素,...}
- var 数组名 [...]类型
- var 数组名 [...]类型 = {元素1,元素2,...} // 让编译器根据元素个数自动推断数组长度
- var 数组名 = [...]类型{元素1,元素2,...} // 让编译器根据元素个数自动推断数组长度
- var 数组名 [...]类型 = [...]类型{元素1,元素2,...} // 让编译器根据元素个数自动推断数组长度
- 数组名 := [...]类型{元素1,元素2,...} // 让编译器根据元素个数自动推断数组长度
- 数组名 := [...]类型{下标: 元素, 下标: 元素, ...} // 让编译器根据元素个数自动推断数组长度
二、数组在内存中的存储方式
-
数组在内存中是连续存储的,每个元素占用相同大小的内存空间。
-
数组的第一个元素的地址和数组的地址相同,后续的元素地址=上一个元素地址 + 数组类型大小。例如,数组类型为int32,每个元素占用4字节内存空间,那么数组第一个元素地址为0x11000,第二个元素地址为0x11004,第三个元素地址为0x11008,以此类推。
-
数组的长度是固定的,定义后不能改变。
-
数组的地址可以通过 &数组名 获取,元素的地址可以通过 &数组名[索引] 获取。
三、数组的使用
-
访问数组元素:数组名[索引],索引从0开始。
-
修改数组元素:数组名[索引] = 新值。
-
数组的长度:len(数组名)。
四、数组的遍历:
-
普通for循环
-
for..range关键字进行遍历。
for index value := range array {
// index是索引,value是元素值。
// index value 都是for循环内的局部变量
// index value 的值在每次循环时都会被重新赋值
// index value 的地址在每次循环时不会改变,因此在循环内如果使用了index value的地址,可能会导致一些问题,例如闭包函数中使用index value的地址,可能会导致所有闭包函数都引用同一个index value变量,从而导致预期之外的结果。
// index value 取名可以以根据实际情况进行修改,例如 for i, v := range array,i是索引,v是元素值。
// 如果不需要使用 index 或 value,可以使用空白标识符 _ 来代替,例如 for _, v := range array,表示只使用元素值,不使用索引。
}
五、数组的注意事项
-
数组的长度是固定的,定义后不能改变,如果需要使用可变长度的集合,可以使用切片(slice)。
-
数组是值类型,赋值和传参会进行值复制,如果数组较大,可能会导致性能问题,可以使用指针来避免值复制,例如传递数组的指针 *[长度]类型。
-
数组的元素类型可以是任意类型,包括基本类型、结构体、指针、接口等,但是不能混合。
-
数组的长度必须是一个常量表达式,不能是变量或函数调用,例如 var arr [n]int 是不合法的,n必须是一个常量。
-
数组的元素默认值为零值,例如 int类型的数组元素默认值为0,string类型的数组元素默认值为"",布尔类型的数组元素默认值为false,指针类型的数组元素默认值为nil。
-
数组的长度可以通过内置函数 len() 获取,例如 len(arr) 返回数组 arr 的长度。
-
数组的元素可以通过索引访问,例如 arr[0] 访问数组 arr 的第一个元素,arr[1] 访问数组 arr 的第二个元素,以此类推。如果超过了数组的长度访问元素,例如 arr[10],会导致运行时错误。
-
数组的元素修改只能修改为同类型的值,例如 int类型的数组只能修改为 int类型的值,string类型的数组只能修改为 string类型的值,否则会导致编译错误。
-
使用数组的步骤:
-
- 定义数组,指定数组的长度和元素类型。
-
- 初始化数组,可以在定义时直接初始化(会先赋值为零值),也可以在定义后逐个赋值。
-
- 访问和修改数组元素,可以通过索引访问和修改数组元素。
-
- 遍历数组,可以使用普通的 for循环或者 for..range循环来遍历数组元素。
-
-
数组的下标从0开始,访问数组元素时需要注意索引越界的问题。
-
如果想在函数内改变数组的内容,需要传递数组的指针,例如 func modifyArray(arr *[长度]类型)。
六、案例
package main
import "fmt"
func main() {
// 定义一个长度为5的int类型数组,并初始化
var arr [5]int = [5]int{1, 2, 3, 4, 5}
// 定义一个长度为3的string类型数组,并初始化
strArr := [3]string{"hello", "world", "golang"}
// 定义一个长度为4的bool类型数组,并初始化
boolArr := [...]bool{0: true, 1: false, 2: true, 3: false}
fmt.Println("整数数组 arr =", arr)
fmt.Println("字符串数组 strArr =", strArr)
fmt.Println("布尔数组 boolArr =", boolArr)
// 定义一个长度为2的结构体类型数组,并初始化
type Person struct {
Name string
Age int
}
var personArr [2]Person = [2]Person{
{Name: "Alice", Age: 30},
{Name: "Bob", Age: 25},
}
fmt.Println("结构体数组 personArr =", personArr)
// 访问数组元素
fmt.Println(arr[0]) // 输出:1
// 访问越界元素会导致运行时错误
// fmt.Println(arr[5]) // 运行时错误:index out of range
// 修改数组元素
arr[0] = 10
fmt.Println(arr[0]) // 输出:10
//修改为不同类型的值会导致编译错误
// arr[0] = "hello" // 编译错误:cannot use "hello" (type string) as type int in assignment
// 遍历数组
// 普通 for 循环
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
// for..range 循环
for index, value := range strArr {
fmt.Printf("index: %d, value: %s\n", index, value)
}
// 如果不需要使用 index,可以使用空白标识符 _ 来代替
for _, val := range strArr {
fmt.Printf("val: %s\n", val)
}
// 数组作为值类型传递
modifyArray(arr)
fmt.Println(arr[0]) // 输出:10,数组没有被修改,因为 modifyArray 函数接收的是数组的值,而不是指针
// 如果需要修改数组的内容,可以传递数组的指针
modifyArray1(&arr)
fmt.Println(arr[0]) // 输出:100,数组被修改,因为 modifyArray1 函数接收的是数组的指针
}
func modifyArray(a [5]int) {
a[0] = 100
}
func modifyArray1(a *[5]int) {
(*a)[0] = 100
}