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 语言,不妨动手写几个小例子:创建数组、初始化、遍历、传参测试。在实践中,你才能真正“摸清”它的脾气。