目录

Go 语言数组

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

数组的定义与声明

Go 中数组的类型表示为 [N]T,其中 N 是数组长度(必须是常量),T 是元素类型。

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 是不同类型,不能互相赋值。

数组初始化

字面量初始化

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
}

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

指定索引初始化

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 循环

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 循环

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 支持多维数组,最常见的是二维数组。

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 中数组是值类型。赋值或传参时,会完整拷贝整个数组。

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)的重要原因之一
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() 函数返回数组的长度,它在编译期就能确定,因为长度是数组类型的一部分。

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() 返回最外层的长度:

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. 缺少实用方法:没有内置的排序、查找等操作
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() 函数。数组作为固定长度的值类型,在内存中连续存储,访问效率高但灵活性不足。在实际开发中,我们更常使用切片来替代数组,因为切片提供了动态长度和引用语义,更加灵活方便。