# Go 语言数组

数组是 Go 语言中最基础的线性数据结构，用于存储固定长度的同类型元素序列。在 Go 中，数组是值类型，长度是类型的一部分。理解数组是掌握切片、字符串等高级类型的前提。

## 数组的定义与声明

Go 中数组的类型表示为 `[N]T`，其中 `N` 是数组长度（必须是常量），`T` 是元素类型。

```go
package main

import "fmt"

func main() {
    // 声明一个长度为 5 的 int 数组，元素默认为零值 0
    var arr [5]int
    fmt.Println(arr) // [0 0 0 0 0]
    fmt.Printf("类型: %T, 长度: %d\n", arr, len(arr))
}
```

数组一旦声明，长度不可改变。`[5]int` 和 `[10]int` 是不同类型，不能互相赋值。

## 数组初始化

### 字面量初始化

```go
package main

import "fmt"

func main() {
    // 完整初始化
    arr1 := [3]int{1, 2, 3}
    fmt.Println(arr1) // [1 2 3]

    // 编译器自动推断长度
    arr2 := [...]int{10, 20, 30, 40}
    fmt.Println(arr2) // [10 20 30 40]
    fmt.Println(len(arr2)) // 4
}
```

`...` 语法让编译器根据初始化元素个数自动推断数组长度。

### 指定索引初始化

```go
package main

import "fmt"

func main() {
    // 只初始化索引 1 和 4，其余元素为零值
    arr := [5]string{"a": "hello", 4: "world"}
    fmt.Println(arr) // [ hello   world]

    // 指定索引 + 自动推断长度
    arr2 := [...]int{0: 100, 3: 200}
    fmt.Println(arr2) // [100 0 0 200]
    fmt.Println(len(arr2)) // 4
}
```

## 数组遍历

### 使用 for 循环

```go
package main

import "fmt"

func main() {
    arr := [4]string{"Go", "Java", "Python", "Rust"}

    for i := 0; i < len(arr); i++ {
        fmt.Printf("arr[%d] = %s\n", i, arr[i])
    }
}
```

### 使用 for-range 循环

```go
package main

import "fmt"

func main() {
    arr := [4]string{"Go", "Java", "Python", "Rust"}

    // 同时获取索引和值
    for i, v := range arr {
        fmt.Printf("索引 %d: %s\n", i, v)
    }

    // 只需要值，忽略索引
    for _, v := range arr {
        fmt.Println(v)
    }

    // 只需要索引
    for i := range arr {
        fmt.Printf("索引: %d\n", i)
    }
}
```

for-range 是 Go 中最常用的遍历方式，代码更简洁，也适用于切片、map、channel 等类型。

## 多维数组

Go 支持多维数组，最常见的是二维数组。

```go
package main

import "fmt"

func main() {
    // 声明并初始化一个 2x3 的二维数组
    matrix := [2][3]int{
        {1, 2, 3},
        {4, 5, 6},
    }

    fmt.Println(matrix)     // [[1 2 3] [4 5 6]]
    fmt.Println(matrix[0])  // [1 2 3]
    fmt.Println(matrix[1][2]) // 6

    // 遍历二维数组
    for i := range matrix {
        for j := range matrix[i] {
            fmt.Printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j])
        }
    }
}
```

多维数组在内存中仍然是连续存储的，`[2][3]int` 等价于 6 个 int 连续排列。

![数组内存布局](https://img.zhaojq.top/20260729161535347.png "数组内存布局")

## 数组的值类型特性

Go 中数组是**值类型**。赋值或传参时，会完整拷贝整个数组。

```go
package main

import "fmt"

func main() {
    a := [3]int{1, 2, 3}
    b := a // 拷贝整个数组

    b[0] = 100

    fmt.Println(a) // [1 2 3]  —— a 不受影响
    fmt.Println(b) // [100 2 3]
}
```

这意味着：
- 大数组的赋值和传参开销较大
- 如果希望共享数据，需要传递数组指针 `*[N]T`
- 这也是 Go 引入切片（slice）的重要原因之一

```go
package main

import "fmt"

func modifyByPointer(arr *[3]int) {
    (*arr)[0] = 999
}

func main() {
    a := [3]int{1, 2, 3}
    modifyByPointer(&a)
    fmt.Println(a) // [999 2 3]
}
```

## 数组的长度

`len()` 函数返回数组的长度，它在编译期就能确定，因为长度是数组类型的一部分。

```go
package main

import "fmt"

func main() {
    arr := [5]int{1, 2, 3, 4, 5}

    fmt.Println(len(arr)) // 5

    // len 返回的是 int 类型
    fmt.Printf("len 的类型: %T\n", len(arr)) // int
}
```

对于多维数组，`len()` 返回最外层的长度：

```go
package main

import "fmt"

func main() {
    matrix := [2][3]int{{1, 2, 3}, {4, 5, 6}}
    fmt.Println(len(matrix))    // 2
    fmt.Println(len(matrix[0])) // 3
}
```

## 数组的局限性

数组虽然简单高效，但存在明显局限：

1. **长度固定**：声明后无法增减元素
2. **值类型拷贝**：大数组传参开销大
3. **无法比较**：Go 中数组不支持 `==` 比较（除非元素类型本身可比较，但实际使用中很少直接比较数组）
4. **缺少实用方法**：没有内置的排序、查找等操作

```go
package main

import "fmt"

func main() {
    var arr [3]int
    // arr = append(arr, 1) // 编译错误！append 只能用于切片

    // 无法动态扩容
    // arr[3] = 100 // panic: index out of range
    fmt.Println(arr)
}
```

正是因为这些局限，Go 提供了**切片（slice）** 作为日常开发中的首选数据结构。切片在数组之上提供了动态长度、引用语义等能力，下一节我们将详细讲解。

## 总结

本篇介绍了 Go 语言数组的核心知识：数组的定义与声明（`[N]T`）、多种初始化方式、for 和 for-range 遍历、多维数组、值类型特性以及 `len()` 函数。数组作为固定长度的值类型，在内存中连续存储，访问效率高但灵活性不足。在实际开发中，我们更常使用切片来替代数组，因为切片提供了动态长度和引用语义，更加灵活方便。

