一、开篇:为什么二维数组是你的“编程神器”?
想象一下,你要在Go语言里记录一个班级的考试成绩。如果只用一维数组,可能得把张三的语文、数学、英语成绩全挤在一行,像这样:
scores := [3]int{90, 85, 88} // 这到底是张三还是李四的成绩?乱套了!
但用二维数组,瞬间清晰:
scores := [2][3]int{
{90, 85, 88}, // 张三的语数外
{78, 92, 80}, // 李四的语数外
}
看,是不是像Excel表格一样直观?二维数组就是“数组的数组”,专门用来处理表格、矩阵、地图这类分层数据。学不会它,你的Go语言技能树可就缺了关键一环!
二、基础入门:二维数组到底是个什么“鬼”?
1. 核心概念:编程界的“俄罗斯套娃”
- 一维数组:像一排糖果,每个格子放一个数据。
- 二维数组:像多排糖果架,每排又是一个糖果数组——套娃式存储!
定义格式很简单:
var 数组名 [行数][列数]类型
比如声明3行2列的整数数组:
var matrix [3][2]int
2. 初始化:五种方法任你选
// 方法1:直接赋值
arr1 := [2][2]int{{1, 2}, {3, 4}}
// 方法2:自动推断行数
arr2 := [...][2]int{{1, 2}, {3, 4}, {5, 6}}
// 方法3:先声明后赋值
var arr3 [2][3]int
arr3[0][1] = 100 // 第0行第1列赋值
// 方法4:部分初始化(未赋值元素自动为零值)
arr4 := [2][3]int{{1}, {2}} // 结果:[[1 0 0] [2 0 0]]
// 方法5:指定索引初始化(Go 1.13+)
arr5 := [2][3]int{0: {1, 2}, 1: {3, 4, 5}}
三、实战演示:玩转二维数组的“十八般武艺”
示例1:遍历——两种循环方式对比
package main
import "fmt"
func main() {
board := [2][3]string{
{"A", "B", "C"},
{"D", "E", "F"},
}
// 方法1:传统for循环(精准控制)
fmt.Println("=== 方法1:坐标遍历 ===")
for i := 0; i < len(board); i++ {
for j := 0; j < len(board[i]); j++ {
fmt.Printf("board[%d][%d]=%s ", i, j, board[i][j])
}
fmt.Println()
}
// 方法2:range遍历(更Go风格)
fmt.Println("\n=== 方法2:range遍历 ===")
for i, row := range board {
for j, val := range row {
fmt.Printf("(%d,%d)=%s ", i, j, val)
}
fmt.Println()
}
}
输出:
=== 方法1:坐标遍历 ===
board[0][0]=A board[0][1]=B board[0][2]=C
board[1][0]=D board[1][1]=E board[1][2]=F
=== 方法2:range遍历 ===
(0,0)=A (0,1)=B (0,2)=C
(1,0)=D (1,1)=E (1,2)=F
示例2:矩阵转置——算法面试常客
func main() {
// 原始矩阵
matrix := [3][2]int{{1, 2}, {3, 4}, {5, 6}}
// 转置:行列互换
var transposed [2][3]int
for i, row := range matrix {
for j, val := range row {
transposed[j][i] = val
}
}
fmt.Println("原矩阵:")
printMatrix(matrix)
fmt.Println("转置后:")
printMatrix(transposed)
}
// 通用打印函数
func printMatrix(matrix interface{}) {
switch m := matrix.(type) {
case [3][2]int:
for _, row := range m {
fmt.Println(row)
}
case [2][3]int:
for _, row := range m {
fmt.Println(row)
}
}
}
输出:
原矩阵:
[1 2]
[3 4]
[5 6]
转置后:
[1 3 5]
[2 4 6]
四、进阶技巧:这些“坑”千万别踩!
1. 内存布局:理解连续存储
二维数组在内存中是连续块,不是指针数组。这意味着:
arr := [2][3]int{}
fmt.Println(&arr[0][0]) // 0xc000018030
fmt.Println(&arr[0][1]) // 0xc000018038(相差8字节,即int64大小)
fmt.Println(&arr[1][0]) // 0xc000018048(紧接上一行末尾)
2. 长度限制:编译时确定
Go的数组长度是类型的一部分,这些会报错:
var n int = 3
var arr [n][n]int // 错误!n必须是常量
rows := 2
cols := 3
var dynamicArr [rows][cols]int // 同样错误!
解决方案:用切片(slice)实现动态二维结构。
3. 性能优化:遍历顺序很重要
// 慢:列遍历(内存不连续)
for j:=0; j<cols; j++ {
for i:=0; i<rows; i++ {
sum += arr[i][j] // 频繁跳行访问
}
}
// 快:行遍历(内存连续)
for i:=0; i<rows; i++ {
for j:=0; j<cols; j++ {
sum += arr[i][j] // 顺序访问,缓存友好
}
}
五、真实应用场景:原来二维数组这么有用!
场景1:游戏地图(井字棋)
func main() {
// 初始化3x3棋盘
board := [3][3]string{}
// 放置棋子
board[0][0] = "X"
board[1][1] = "O"
board[2][2] = "X"
// 打印棋盘
fmt.Println("井字棋棋盘:")
for _, row := range board {
for _, cell := range row {
if cell == "" {
fmt.Print("_ ")
} else {
fmt.Print(cell + " ")
}
}
fmt.Println()
}
}
场景2:成绩统计表
func main() {
// 3个学生,4门课程
scores := [3][4]float64{
{85.5, 92.0, 78.5, 90.0},
{88.0, 95.5, 82.0, 79.5},
{92.5, 89.0, 94.5, 87.0},
}
// 计算每个学生平均分
fmt.Println("学生平均分:")
for i, student := range scores {
total := 0.0
for _, score := range student {
total += score
}
avg := total / float64(len(student))
fmt.Printf("学生%d: %.2f\n", i+1, avg)
}
// 计算每门课程平均分
fmt.Println("\n课程平均分:")
for j := 0; j < len(scores[0]); j++ {
total := 0.0
for i := 0; i < len(scores); i++ {
total += scores[i][j]
}
avg := total / float64(len(scores))
fmt.Printf("课程%d: %.2f\n", j+1, avg)
}
}
六、常见问题答疑区
Q1:二维数组能动态扩容吗?
不行!数组长度固定。需要动态大小请用切片:
// 二维切片示例
matrix := make([][]int, 3)
for i := range matrix {
matrix[i] = make([]int, 4)
}
Q2:如何复制二维数组?
直接赋值是深拷贝:
arr1 := [2][2]int{{1, 2}, {3, 4}}
arr2 := arr1 // 完整复制,修改arr2不影响arr1
arr2[0][0] = 99
fmt.Println(arr1[0][0]) // 仍然是1
Q3:能创建三维数组吗?
当然!原理相同:
cube := [2][3][4]int{} // 2层3行4列的立方体
七、总结:你的二维数组修炼手册
二维数组不是什么洪水猛兽,它就是数据组织的艺术。记住几个关键点:
- 声明别忘双括号:
[行][列]类型 - 初始化花样多:完整、部分、自动推断任选
- 遍历首选range:代码更简洁安全
- 内存布局要理解:行遍历效率更高
- 固定长度是限制:动态需求用切片
现在,打开你的编辑器,用二维数组写个贪吃蛇地图或者五子棋棋盘吧!编程就像搭乐高——理解了基础积木,你就能创造整个世界。

被折叠的 条评论
为什么被折叠?



