GO语言基础教程(52)Go数组之二维数组:Go语言二维数组全攻略:编程界的“乐高大师”速成指南!

一、开篇:为什么二维数组是你的“编程神器”?

想象一下,你要在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列的立方体

七、总结:你的二维数组修炼手册

二维数组不是什么洪水猛兽,它就是数据组织的艺术。记住几个关键点:

  1. 声明别忘双括号[行][列]类型
  2. 初始化花样多:完整、部分、自动推断任选
  3. 遍历首选range:代码更简洁安全
  4. 内存布局要理解:行遍历效率更高
  5. 固定长度是限制:动态需求用切片

现在,打开你的编辑器,用二维数组写个贪吃蛇地图或者五子棋棋盘吧!编程就像搭乐高——理解了基础积木,你就能创造整个世界。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值