基本概念
在 Go 语言中,数组(Array)是一种固定长度的、相同类型的元素序列。数组的长度在声明时就已经确定,并且不能改变。数组的每个元素可以通过索引访问,索引从 0 开始。
数组的声明和初始化:
在 Go 语言中,数组的声明和初始化有多种方式,以下是几种常见的方式:
1. 声明并初始化数组
方式一:使用 var
关键字声明并初始化
csharp
代码解读
复制代码
var arr [5]int = [5]int{1, 2, 3, 4, 5}
方式二:使用短变量声明并初始化
go
代码解读
复制代码
arr := [5]int{1, 2, 3, 4, 5}
2. 声明数组但不初始化
csharp
代码解读
复制代码
var arr [5]int
这种方式声明的数组,所有元素会被初始化为该类型的零值(例如,int
类型的零值是 0
)。
3. 使用省略号 ...
自动推断数组长度
go
代码解读
复制代码
arr := [...]int{1, 2, 3, 4, 5}
这种方式会根据初始化列表中的元素个数自动推断数组的长度。
4. 部分初始化数组
go
代码解读
复制代码
arr := [5]int{1, 2}
这种方式会初始化前两个元素为 1
和 2
,其余元素会被初始化为零值。
5. 指定索引初始化数组
css
代码解读
复制代码
arr := [5]int{0: 1, 4: 5}
这种方式会初始化索引为 0
的元素为 1
,索引为 4
的元素为 5
,其余元素会被初始化为零值。
6. 多维数组的声明和初始化
less
代码解读
复制代码
var arr [2][3]int
arr := [2][3]int{{1, 2, 3}, {4, 5, 6}}
多维数组的声明和初始化与一维数组类似,只是嵌套了更多的维度。
示例代码:
go
代码解读
复制代码
package main
import "fmt"
func main() {
// 方式一:使用 var 关键字声明并初始化
var arr1 [5]int = [5]int{1, 2, 3, 4, 5}
fmt.Println(arr1)
// 方式二:使用短变量声明并初始化
arr2 := [5]int{1, 2, 3, 4, 5}
fmt.Println(arr2)
// 方式三:声明数组但不初始化
var arr3 [5]int
fmt.Println(arr3)
// 方式四:使用省略号自动推断数组长度
arr4 := [...]int{1, 2, 3, 4, 5}
fmt.Println(arr4)
// 方式五:部分初始化数组
arr5 := [5]int{1, 2}
fmt.Println(arr5)
// 方式六:指定索引初始化数组
arr6 := [5]int{0: 1, 4: 5}
fmt.Println(arr6)
// 方式七:多维数组的声明和初始化
var arr7 [2][3]int
arr8 := [2][3]int{{1, 2, 3}, {4, 5, 6}}
fmt.Println(arr7)
fmt.Println(arr8)
}
数组的内存分析:
在 Go 语言中,数组的内存分配和布局相对简单且直观。数组是一个固定长度的、相同类型的元素序列,其内存布局是连续的。
1. 内存分配
当声明一个数组时,Go 语言会在内存中分配一块连续的内存区域,用于存储数组的元素。数组的长度在声明时就已经确定,并且不能改变。
例如,声明一个包含 5 个整数的数组:
csharp
代码解读
复制代码
var arr [5]int
在这个例子中,Go 语言会在内存中分配一块连续的内存区域,大小为 5 * sizeof(int)
字节。假设 int
类型在当前平台上占用 4 个字节,那么这块内存区域的大小就是 5 * 4 = 20
字节。
2. 内存布局
数组的内存布局是线性的,即数组的每个元素在内存中是连续存储的。数组的第一个元素存储在内存的起始位置,第二个元素紧随其后,依此类推。
例如,对于数组 arr := [5]int{1, 2, 3, 4, 5}
,其内存布局如下:
css
代码解读
复制代码
内存地址: | arr[0] | arr[1] | arr[2] | arr[3] | arr[4] |
值: | 1 | 2 | 3 | 4 | 5 |
每个元素的内存地址可以通过数组的基地址(即数组的第一个元素的地址)加上元素的索引和元素类型的大小来计算。
3. 访问数组元素
由于数组的内存布局是连续的,访问数组元素非常高效。通过索引访问数组元素时,Go 语言会直接计算出该元素的内存地址,并从该地址读取或写入数据。
例如,访问数组 arr
的第三个元素:
go
代码解读
复制代码
value := arr[2]
Go 语言会计算出 arr[2]
的内存地址为 arr
的基地址加上 2 * sizeof(int)
字节,然后从该地址读取数据。
4. 数组的传递
在 Go 语言中,数组是值类型,这意味着当数组作为参数传递给函数时,会进行一次完整的复制。如果数组很大,这可能会导致性能问题。
例如:
go
代码解读
复制代码
func modifyArray(arr [5]int) {
arr[0] = 100
}
func main() {
arr := [5]int{1, 2, 3, 4, 5}
modifyArray(arr)
fmt.Println(arr) // 输出: [1 2 3 4 5]
}
在这个例子中,modifyArray
函数接收的是 arr
的一个副本,因此修改副本不会影响原始数组。
数组的遍历:
在 Go 语言中,遍历数组有多种方式,常用的方法包括使用 for
循环和 range
关键字。以下是几种常见的遍历数组的方法:
1. 使用 for
循环和索引遍历数组
这是最基本的遍历方式,通过索引访问数组的每个元素。
go
代码解读
复制代码
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
}
2. 使用 range
关键字遍历数组
range
关键字可以方便地遍历数组,它会返回索引和对应的元素值。
go
代码解读
复制代码
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
for index, value := range arr {
fmt.Printf("Index: %d, Value: %d\n", index, value)
}
}
3. 只使用 range
遍历数组的值
如果你只关心数组的值而不关心索引,可以使用 _
忽略索引。
go
代码解读
复制代码
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
for _, value := range arr {
fmt.Println(value)
}
}
4. 只使用 range
遍历数组的索引
如果你只关心数组的索引而不关心值,可以只使用索引。
go
代码解读
复制代码
package main
import "fmt"
func main() {
arr := [5]int{1, 2, 3, 4, 5}
for index := range arr {
fmt.Println(index)
}
}
5. 使用 for
循环和 range
遍历多维数组
对于多维数组,可以嵌套使用 for
循环和 range
关键字。
css
代码解读
复制代码
package main
import "fmt"
func main() {
arr := [2][3]int{{1, 2, 3}, {4, 5, 6}}
for i := 0; i < len(arr); i++ {
for j := 0; j < len(arr[i]); j++ {
fmt.Printf("arr[%d][%d] = %d\n", i, j, arr[i][j])
}
}
// 使用 range 遍历多维数组
for i, row := range arr {
for j, value := range row {
fmt.Printf("arr[%d][%d] = %d\n", i, j, value)
}
}
}