导航菜单

数组

定义与初始化

Go 中数组的定义方式非常灵活,支持多种声明和初始化语法。

基本声明

// 声明一个长度为 5 的整型数组,元素默认初始化为零值 0
var arr1 [5]int
fmt.Println(arr1) // [0 0 0 0 0]

数组内存布局可视化

64位系统

连续内存空间

注:在64位系统上,int类型占用8字节;32位系统上占用4字节

空闲
0xC0000
空闲
0xC0008
[0] 0
0xC0010
[1] 0
0xC0018
[2] 0
0xC0020
[3] 0
0xC0028
[4] 0
0xC0030
空闲
0xC0038
空闲
0xC0040
内存块布局(8 字节 = 64 位)
0xC0010
B0
0xC0011
B1
0xC0012
B2
0xC0013
B3
0xC0014
B4
0xC0015
B5
0xC0016
B6
0xC0017
B7
arr1[0] = 0
零值(Zero Value)

Go 语言中,声明但未显式初始化的变量会被自动赋予该类型的零值。对于数值类型,零值是 0;对于字符串,零值是空字符串 "";对于布尔类型,零值是 false;对于数组,所有元素都会被初始化为其元素类型的零值。

使用字面量初始化

// 方式一:指定所有元素的值
var arr2 = [5]int{1, 2, 3, 4, 5}
fmt.Println(arr2) // [1 2 3 4 5]

// 方式二:使用短变量声明
arr3 := [5]int{10, 20, 30, 40, 50}
fmt.Println(arr3) // [10 20 30 40 50]

// 方式三:让编译器根据初始值个数推断长度(使用 ...)
arr4 := [...]int{1, 2, 3}
fmt.Println(arr4)       // [1 2 3]
fmt.Println(len(arr4))  // 3

指定索引初始化

// 只初始化指定索引位置的元素,其余为默认零值
arr5 := [5]int{0: 10, 4: 50}
fmt.Println(arr5) // [10 0 0 0 50]

// 用于枚举常量
const (
    Sunday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

var days = [...]string{Sunday: "周日", Monday: "周一", Tuesday: "周二", Wednesday: "周三", Thursday: "周四", Friday: "周五", Saturday: "周六"}
fmt.Println(days) // [周日 周一 周二 周三 周四 周五 周六]

长度是类型的一部分

这是 Go 数组与 C/Java 数组的一个核心区别,也是初学者最容易犯的错误之一。

数组长度是类型的一部分

在 Go 中,[3]int[5]int 是两个完全不同的类型。它们之间不能直接赋值,也不能互相比较。数组的长度在编译期确定,属于数组类型的一部分,不能在运行时改变。

var a [3]int = [3]int{1, 2, 3}
var b [5]int = [5]int{1, 2, 3, 4, 5}

// 以下代码会编译报错:不能将 [5]int 赋值给 [3]int
// a = b // 编译错误:cannot use b (type [5]int) as type [3]int in assignment

// 以下代码也会编译报错:类型不同,不能比较
// fmt.Println(a == b) // 编译错误:invalid operation

fmt.Printf("a 的类型: %T\n", a) // [3]int
fmt.Printf("b 的类型: %T\n", b) // [5]int
original := [3]int{1, 2, 3}
copy := original     // 值拷贝:复制了整个数组
copy[0] = 100
fmt.Println(original) // [1 2 3]  —— 原数组未被修改
fmt.Println(copy)     // [100 2 3]

多维数组

Go 支持多维数组,最常用的是二维数组,它可以表示矩阵、表格等数据结构。

声明与初始化

// 声明一个 2 行 3 列的二维数组
var matrix [2][3]int
fmt.Println(matrix) // [[0 0 0] [0 0 0]]

// 使用字面量初始化
grid := [2][3]int{
    {1, 2, 3},
    {4, 5, 6},
}
fmt.Println(grid) // [[1 2 3] [4 5 6]]

// 访问元素
fmt.Println(grid[0][1]) // 2
fmt.Println(grid[1][2]) // 6

// 修改元素
grid[1][0] = 40
fmt.Println(grid) // [[1 2 3] [40 5 6]]

三维及以上

// 3x2x2 的三维数组
cube := [3][2][2]int{
    {{1, 2}, {3, 4}},
    {{5, 6}, {7, 8}},
    {{9, 10}, {11, 12}},
}
fmt.Println(cube[1][0][1]) // 6

遍历数组

Go 提供了两种遍历数组的方式:经典 for 循环和 range 循环。

使用 for 循环(通过索引)

arr := [5]int{10, 20, 30, 40, 50}

for i := 0; i < len(arr); i++ {
    fmt.Printf("索引 %d: %d\n", i, arr[i])
}
// 输出:
// 索引 0: 10
// 索引 1: 20
// ...

使用 range 循环(推荐)

arr := [5]string{"Go", "Python", "Rust", "Java", "C++"}

// 方式一:同时获取索引和值
for index, value := range arr {
    fmt.Printf("索引 %d: %s\n", index, value)
}

// 方式二:只获取值(使用空白标识符 _ 忽略索引)
for _, value := range arr {
    fmt.Printf("值: %s\n", value)
}

// 方式三:只获取索引
for index := range arr {
    fmt.Printf("索引: %d\n", index)
}

遍历多维数组

matrix := [2][3]int{
    {1, 2, 3},
    {4, 5, 6},
}

for i, row := range matrix {
    for j, val := range row {
        fmt.Printf("matrix[%d][%d] = %d\n", i, j, val)
    }
}
arr := [3]int{1, 2, 3}
for _, v := range arr {
    v = v * 10 // 修改的是副本,不影响原数组
}
fmt.Println(arr) // [1 2 3] —— 未被修改

// 正确的修改方式
for i := range arr {
    arr[i] = arr[i] * 10
}
fmt.Println(arr) // [10 20 30]

数组作为函数参数

因为数组是值类型,所以将数组传递给函数时会进行完整的拷贝。对于大数组来说,这会带来性能开销。

func modifyArray(arr [3]int) {
    arr[0] = 999 // 修改的是函数内的副本
}

func main() {
    original := [3]int{1, 2, 3}
    modifyArray(original)
    fmt.Println(original) // [1 2 3] —— 原数组未被修改
}

如果希望在函数内修改原数组,可以传递数组的指针

func modifyArrayByPointer(arr *[3]int) {
    arr[0] = 999 // 通过指针修改原数组
}

func main() {
    original := [3]int{1, 2, 3}
    modifyArrayByPointer(&original)
    fmt.Println(original) // [999 2 3] —— 原数组已被修改
}

数组的局限性

Go 中的数组在实际开发中直接使用频率不高,主要存在以下局限性:

局限性说明
长度固定数组一旦声明,长度不可改变,缺乏灵活性
长度是类型的一部分不同长度的数组是不同类型,难以编写通用的函数
值拷贝开销作为参数传递时会完整拷贝,对大数组性能不友好
无法动态扩缩无法在运行时增加或减少元素

练习题

练习 1:数组基本操作

编写一个程序,声明一个长度为 10 的 int 数组,将数组中的每个元素设置为其索引的平方(即 arr[i] = i * i),然后打印整个数组。

参考答案
package main

import "fmt"

func main() {
    var squares [10]int
    for i := 0; i < len(squares); i++ {
        squares[i] = i * i
    }
    fmt.Println(squares) // [0 1 4 9 16 25 36 49 64 81]
}

练习 2:矩阵转置

编写一个函数,接收一个 3×3 的 int 矩阵(二维数组),返回其转置矩阵(行列互换)。

参考答案
package main

import "fmt"

func transpose(matrix [3][3]int) [3][3]int {
    var result [3][3]int
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            result[i][j] = matrix[j][i]
        }
    }
    return result
}

func main() {
    matrix := [3][3]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }

    fmt.Println("原始矩阵:")
    for _, row := range matrix {
        fmt.Println(row)
    }

    result := transpose(matrix)
    fmt.Println("\n转置矩阵:")
    for _, row := range result {
        fmt.Println(row)
    }
}
// 输出:
// 原始矩阵:
// [1 2 3]
// [4 5 6]
// [7 8 9]
//
// 转置矩阵:
// [1 4 7]
// [2 5 8]
// [3 6 9]

练习 3:数组值传递验证

编写一个程序验证数组是值类型:创建一个数组,赋值给另一个变量,修改其中一个变量的元素,观察另一个变量是否受影响。

参考答案
package main

import "fmt"

func main() {
    arr1 := [4]string{"春", "夏", "秋", "冬"}
    arr2 := arr1 // 值拷贝

    arr2[0] = "Spring"

    fmt.Println("arr1:", arr1) // arr1: [春 夏 秋 冬]
    fmt.Println("arr2:", arr2) // arr2: [Spring 夏 秋 冬]

    // arr1 不受 arr2 修改的影响,证明数组是值类型
}

搜索