Go 语言数组(超详细)

Go 语言数组:从入门到实战

在 Go 语言中,数组是一种基础但非常重要的数据结构。它就像一个固定大小的“盒子”,可以存放相同类型的多个值。当你需要处理一系列相关数据时,比如学生成绩、天气温度、订单编号等,数组就是你最可靠的伙伴。

与一些动态语言不同,Go 语言中的数组长度是固定的。这意味着你声明数组时就必须确定它的大小,之后不能再改变。这听起来有点严格,但正是这种“确定性”让 Go 语言在性能和安全性上表现优异。想象一下,你买了一个 10 个格子的收纳盒,你只能放 10 个东西,多了不行,少了也没法扩展——这就是 Go 语言数组的特性。

如果你刚开始接触 Go,可能会觉得数组“太死板”。但别急,随着你深入使用,会发现它的设计哲学非常清晰:提前规划,避免运行时错误。这正是 Go 语言“少即是多”理念的体现。

接下来,我们将一步步带你掌握 Go 语言数组的核心知识,从创建、初始化到遍历、传参,再到实际应用场景,让你真正“用得明白、用得高效”。


创建数组与初始化

在 Go 语言中,声明一个数组的语法非常清晰。基本格式是:var 数组名 [长度]数据类型

例如,我们要创建一个长度为 5 的整数数组,可以这样写:

var scores [5]int

这行代码的意思是:声明一个名为 scores 的数组,它有 5 个位置,每个位置存放一个整数(int 类型)。注意,此时数组中的每个元素都是默认值(int 类型的零值是 0),所以 scores 的内容是 [0, 0, 0, 0, 0]

如果你希望在创建时就给数组赋初值,Go 提供了更简洁的方式:

// 使用字面量初始化
var numbers = [3]int{10, 20, 30}

这里我们直接用 {} 包裹了三个值,Go 会自动推断数组长度为 3。你也可以省略长度,让编译器自动计算:

// 省略长度,由初始化值决定
var ages = [...]int{18, 25, 30, 45}
// 等价于 [4]int{18, 25, 30, 45}

这种写法特别适合你已经知道数据内容,但不确定具体长度的情况。

⚠️ 注意:数组长度是类型的一部分。也就是说,[3]int[4]int 是两种完全不同的类型,不能互相赋值。


数组的访问与修改

数组的元素通过索引访问,索引从 0 开始。例如,scores[0] 是第一个元素,scores[4] 是最后一个(如果长度是 5)。

来看一个完整的例子:

package main

import "fmt"

func main() {
    // 创建一个长度为 4 的字符串数组
    var names [4]string

    // 给第 0 个位置赋值
    names[0] = "Alice"
    // 给第 1 个位置赋值
    names[1] = "Bob"
    // 给第 2 个位置赋值
    names[2] = "Charlie"
    // 给第 3 个位置赋值
    names[3] = "Diana"

    // 输出所有名字
    fmt.Println("所有名字:", names)
}

输出结果:

所有名字: [Alice Bob Charlie Diana]

你也可以在初始化时直接赋值:

// 直接初始化并赋值
fruits := [3]string{"苹果", "香蕉", "橙子"}

// 修改某个元素
fruits[1] = "葡萄"
fmt.Println("水果列表:", fruits) // 输出: [苹果 葡萄 橙子]

💡 小贴士:数组的索引不能越界。如果你写 names[5],Go 会在编译阶段就报错,这避免了运行时崩溃,是 Go 语言安全性的体现。


数组遍历:用 for 循环轻松搞定

遍历数组是日常开发中最常见的操作之一。Go 语言提供了两种主要的遍历方式:传统的 for 循环和 for-range 循环。

使用传统 for 循环

// 定义一个整数数组
numbers := [5]int{1, 2, 3, 4, 5}

// 使用传统的 for 循环遍历
for i := 0; i < len(numbers); i++ {
    fmt.Printf("索引 %d 的值是 %d\n", i, numbers[i])
}

输出:

索引 0 的值是 1
索引 1 的值是 2
索引 2 的值是 3
索引 3 的值是 4
索引 4 的值是 5

这里 len(numbers) 返回数组长度,是 5

使用 for-range 循环(推荐方式)

// 使用 for-range 遍历
for index, value := range numbers {
    fmt.Printf("位置 %d 的值是 %d\n", index, value)
}

输出与上面相同,但代码更简洁,可读性更高。

📌 range 返回两个值:索引(index)和值(value)。如果你只关心值,可以忽略索引,用下划线 _ 表示:

for _, value := range numbers {
    fmt.Println("值:", value)
}

数组作为函数参数:值传递的陷阱

在 Go 语言中,数组是值类型,这意味着当你把数组传给函数时,传递的是它的副本,而不是引用。

来看一个例子:

func modifyArray(arr [3]int) {
    arr[0] = 999 // 修改副本
    fmt.Println("函数内修改后:", arr)
}

func main() {
    data := [3]int{10, 20, 30}
    fmt.Println("调用前:", data)

    modifyArray(data) // 传入副本

    fmt.Println("调用后:", data) // 原数组未被修改
}

输出:

调用前: [10 20 30]
函数内修改后: [999 20 30]
调用后: [10 20 30]

可以看到,原数组 data 没有被修改。这是因为 modifyArray 接收的是 data 的一份拷贝。

❗ 这是一个常见陷阱!如果你希望函数能修改原数组,必须使用指针,或者改用切片(slice)。切片是 Go 中更常用、更灵活的数组替代方案。


Go 语言数组的典型应用场景

虽然数组长度固定,但它在某些场景下依然非常实用:

1. 保存固定数量的配置项

// 定义一个配置数组,保存 3 个环境参数
var envConfig [3]string = [3]string{"dev", "test", "prod"}

// 遍历打印
for i, env := range envConfig {
    fmt.Printf("环境 %d: %s\n", i+1, env)
}

2. 存储温度数据(比如 7 天的天气)

// 存储一周的平均气温(单位:摄氏度)
var weeklyTemps [7]float64

weeklyTemps[0] = 20.5
weeklyTemps[1] = 22.1
weeklyTemps[2] = 19.8
weeklyTemps[3] = 23.4
weeklyTemps[4] = 25.0
weeklyTemps[5] = 24.2
weeklyTemps[6] = 21.0

// 计算平均温度
var sum float64
for _, temp := range weeklyTemps {
    sum += temp
}
avg := sum / float64(len(weeklyTemps))
fmt.Printf("本周平均气温: %.1f°C\n", avg)

3. 实现简单的缓存队列(固定大小)

// 用数组实现一个容量为 5 的简单队列
type FixedQueue struct {
    data [5]int
    size int
    front int
    rear int
}

func (q *FixedQueue) Enqueue(value int) bool {
    if q.size >= 5 {
        return false // 队列已满
    }
    q.data[q.rear] = value
    q.rear = (q.rear + 1) % 5
    q.size++
    return true
}

func (q *FixedQueue) Dequeue() (int, bool) {
    if q.size == 0 {
        return 0, false
    }
    value := q.data[q.front]
    q.front = (q.front + 1) % 5
    q.size--
    return value, true
}

这个例子展示了数组在实现数据结构时的实用性,尤其是对性能要求高的场景。


总结:Go 语言数组的核心要点

Go 语言数组虽然“固定”但“可靠”。它不适用于动态扩容的场景,但在需要确定大小、追求性能和安全性的场合,它是理想选择。

我们回顾一下重点:

  • 数组长度在声明时就确定,不能改变。
  • 数组是值类型,传参时会复制整个数组。
  • 使用 len() 获取数组长度,索引从 0 开始。
  • 推荐使用 for-range 遍历数组,代码更简洁。
  • 适用于配置项、固定数据集、底层算法等场景。

虽然现代 Go 开发中,切片(slice)使用频率更高,但理解数组是掌握切片和内存管理的基础。就像盖房子前要打地基一样,掌握 Go 语言数组,是你迈向高级 Go 开发的坚实一步。

如果你正在学习 Go 语言,不妨动手写几个小例子:创建数组、初始化、遍历、传参测试。在实践中,你才能真正“摸清”它的脾气。