/* 01-arrays.go - Go 语言数组详解 学习目标: 1. 理解数组的概念和特性 2. 掌握数组的声明和初始化 3. 学会数组的基本操作 4. 了解数组的内存布局 5. 掌握数组的实际应用场景 知识点: - 数组的定义和特性 - 数组的声明和初始化方式 - 数组的访问和修改 - 数组的长度和容量 - 多维数组 - 数组的比较和拷贝 - 数组 vs 切片的区别 */ package main import "fmt" func main() { fmt.Println("=== Go 语言数组详解 ===\n") // 演示数组的基本概念 demonstrateBasicArrays() // 演示数组的声明和初始化 demonstrateArrayDeclaration() // 演示数组的操作 demonstrateArrayOperations() // 演示多维数组 demonstrateMultiDimensionalArrays() // 演示数组的特性 demonstrateArrayProperties() // 演示数组的实际应用 demonstratePracticalApplications() // 演示数组 vs 切片 demonstrateArrayVsSlice() } // demonstrateBasicArrays 演示数组的基本概念 func demonstrateBasicArrays() { fmt.Println("1. 数组的基本概念:") // 数组的基本特性 fmt.Printf(" 数组的基本特性:\n") fmt.Printf(" - 固定长度的元素序列\n") fmt.Printf(" - 元素类型相同\n") fmt.Printf(" - 长度是类型的一部分\n") fmt.Printf(" - 零值是所有元素都为零值的数组\n") fmt.Printf(" - 值类型,赋值和传参会拷贝整个数组\n") // 基本数组示例 fmt.Printf(" 基本数组示例:\n") var numbers [5]int // 声明长度为5的整数数组 fmt.Printf(" 声明数组: %v\n", numbers) fmt.Printf(" 数组长度: %d\n", len(numbers)) fmt.Printf(" 数组类型: %T\n", numbers) // 数组元素访问 numbers[0] = 10 numbers[1] = 20 numbers[2] = 30 fmt.Printf(" 修改元素后: %v\n", numbers) fmt.Printf(" 第一个元素: %d\n", numbers[0]) fmt.Printf(" 最后一个元素: %d\n", numbers[len(numbers)-1]) fmt.Println() } // demonstrateArrayDeclaration 演示数组的声明和初始化 func demonstrateArrayDeclaration() { fmt.Println("2. 数组的声明和初始化:") // 方式1: 声明后赋值 fmt.Printf(" 方式1 - 声明后赋值:\n") var arr1 [3]int arr1[0] = 1 arr1[1] = 2 arr1[2] = 3 fmt.Printf(" arr1: %v\n", arr1) // 方式2: 声明时初始化 fmt.Printf(" 方式2 - 声明时初始化:\n") var arr2 = [3]int{10, 20, 30} fmt.Printf(" arr2: %v\n", arr2) // 方式3: 短变量声明 fmt.Printf(" 方式3 - 短变量声明:\n") arr3 := [4]string{"Go", "Python", "Java", "C++"} fmt.Printf(" arr3: %v\n", arr3) // 方式4: 自动推断长度 fmt.Printf(" 方式4 - 自动推断长度:\n") arr4 := [...]int{1, 2, 3, 4, 5} // ... 让编译器计算长度 fmt.Printf(" arr4: %v (长度: %d)\n", arr4, len(arr4)) // 方式5: 指定索引初始化 fmt.Printf(" 方式5 - 指定索引初始化:\n") arr5 := [5]int{1: 10, 3: 30, 4: 40} // 指定索引1,3,4的值 fmt.Printf(" arr5: %v\n", arr5) // 方式6: 混合初始化 fmt.Printf(" 方式6 - 混合初始化:\n") arr6 := [...]string{0: "first", 2: "third", "fourth"} // 混合指定索引和顺序 fmt.Printf(" arr6: %v (长度: %d)\n", arr6, len(arr6)) // 不同类型的数组 fmt.Printf(" 不同类型的数组:\n") boolArray := [3]bool{true, false, true} floatArray := [4]float64{1.1, 2.2, 3.3, 4.4} stringArray := [2]string{"hello", "world"} fmt.Printf(" 布尔数组: %v\n", boolArray) fmt.Printf(" 浮点数组: %v\n", floatArray) fmt.Printf(" 字符串数组: %v\n", stringArray) fmt.Println() } // demonstrateArrayOperations 演示数组的操作 func demonstrateArrayOperations() { fmt.Println("3. 数组的操作:") // 数组遍历 fmt.Printf(" 数组遍历:\n") fruits := [4]string{"apple", "banana", "cherry", "date"} // 使用索引遍历 fmt.Printf(" 使用索引遍历:\n") for i := 0; i < len(fruits); i++ { fmt.Printf(" fruits[%d] = %s\n", i, fruits[i]) } // 使用 range 遍历 fmt.Printf(" 使用 range 遍历:\n") for index, value := range fruits { fmt.Printf(" fruits[%d] = %s\n", index, value) } // 只要值,忽略索引 fmt.Printf(" 只要值,忽略索引:\n") for _, fruit := range fruits { fmt.Printf(" 水果: %s\n", fruit) } // 只要索引,忽略值 fmt.Printf(" 只要索引,忽略值:\n") for index := range fruits { fmt.Printf(" 索引: %d\n", index) } // 数组修改 fmt.Printf(" 数组修改:\n") numbers := [5]int{1, 2, 3, 4, 5} fmt.Printf(" 原始数组: %v\n", numbers) // 修改单个元素 numbers[2] = 99 fmt.Printf(" 修改索引2: %v\n", numbers) // 批量修改 for i := range numbers { numbers[i] *= 2 } fmt.Printf(" 全部乘以2: %v\n", numbers) // 数组查找 fmt.Printf(" 数组查找:\n") target := "cherry" found := false foundIndex := -1 for i, fruit := range fruits { if fruit == target { found = true foundIndex = i break } } if found { fmt.Printf(" 找到 '%s' 在索引 %d\n", target, foundIndex) } else { fmt.Printf(" 未找到 '%s'\n", target) } fmt.Println() }// de monstrateMultiDimensionalArrays 演示多维数组 func demonstrateMultiDimensionalArrays() { fmt.Println("4. 多维数组:") // 二维数组 fmt.Printf(" 二维数组:\n") // 声明和初始化二维数组 var matrix [3][4]int fmt.Printf(" 声明 3x4 矩阵: %v\n", matrix) // 初始化二维数组 matrix2 := [2][3]int{ {1, 2, 3}, {4, 5, 6}, } fmt.Printf(" 初始化 2x3 矩阵:\n") for i, row := range matrix2 { fmt.Printf(" 行 %d: %v\n", i, row) } // 访问和修改二维数组元素 fmt.Printf(" 访问元素 matrix2[1][2]: %d\n", matrix2[1][2]) matrix2[0][1] = 99 fmt.Printf(" 修改后 matrix2[0][1]: %d\n", matrix2[0][1]) // 遍历二维数组 fmt.Printf(" 遍历二维数组:\n") for i := 0; i < len(matrix2); i++ { for j := 0; j < len(matrix2[i]); j++ { fmt.Printf(" matrix2[%d][%d] = %d\n", i, j, matrix2[i][j]) } } // 使用 range 遍历二维数组 fmt.Printf(" 使用 range 遍历:\n") for i, row := range matrix2 { for j, value := range row { fmt.Printf(" [%d][%d] = %d\n", i, j, value) } } // 三维数组 fmt.Printf(" 三维数组:\n") cube := [2][2][2]int{ { {1, 2}, {3, 4}, }, { {5, 6}, {7, 8}, }, } fmt.Printf(" 3D 数组:\n") for i, plane := range cube { fmt.Printf(" 平面 %d:\n", i) for j, row := range plane { fmt.Printf(" 行 %d: %v\n", j, row) } } // 实际应用:游戏棋盘 fmt.Printf(" 实际应用 - 井字棋棋盘:\n") board := [3][3]string{ {"X", "O", "X"}, {"O", "X", "O"}, {"X", "O", "X"}, } fmt.Printf(" 井字棋棋盘:\n") for i, row := range board { fmt.Printf(" ") for j, cell := range row { fmt.Printf("%s", cell) if j < len(row)-1 { fmt.Printf(" | ") } } fmt.Printf("\n") if i < len(board)-1 { fmt.Printf(" ---------\n") } } fmt.Println() } // demonstrateArrayProperties 演示数组的特性 func demonstrateArrayProperties() { fmt.Println("5. 数组的特性:") // 数组是值类型 fmt.Printf(" 数组是值类型:\n") arr1 := [3]int{1, 2, 3} arr2 := arr1 // 拷贝整个数组 fmt.Printf(" 原数组 arr1: %v\n", arr1) fmt.Printf(" 拷贝数组 arr2: %v\n", arr2) // 修改拷贝不影响原数组 arr2[0] = 99 fmt.Printf(" 修改 arr2[0] 后:\n") fmt.Printf(" arr1: %v (未改变)\n", arr1) fmt.Printf(" arr2: %v (已改变)\n", arr2) // 数组比较 fmt.Printf(" 数组比较:\n") a := [3]int{1, 2, 3} b := [3]int{1, 2, 3} c := [3]int{1, 2, 4} fmt.Printf(" a == b: %t\n", a == b) fmt.Printf(" a == c: %t\n", a == c) fmt.Printf(" a != c: %t\n", a != c) // 注意:不同长度的数组是不同类型,不能比较 // var d [4]int // fmt.Println(a == d) // 编译错误 // 数组作为函数参数 fmt.Printf(" 数组作为函数参数:\n") original := [3]int{10, 20, 30} fmt.Printf(" 调用前: %v\n", original) modifyArrayByValue(original) fmt.Printf(" 值传递后: %v (未改变)\n", original) modifyArrayByPointer(&original) fmt.Printf(" 指针传递后: %v (已改变)\n", original) // 数组的零值 fmt.Printf(" 数组的零值:\n") var zeroIntArray [3]int var zeroStringArray [2]string var zeroBoolArray [2]bool fmt.Printf(" int 数组零值: %v\n", zeroIntArray) fmt.Printf(" string 数组零值: %v\n", zeroStringArray) fmt.Printf(" bool 数组零值: %v\n", zeroBoolArray) // 数组长度是编译时常量 fmt.Printf(" 数组长度是编译时常量:\n") const size = 5 constArray := [size]int{1, 2, 3, 4, 5} fmt.Printf(" 使用常量定义数组: %v\n", constArray) fmt.Println() } // demonstratePracticalApplications 演示数组的实际应用 func demonstratePracticalApplications() { fmt.Println("6. 数组的实际应用:") // 应用1: 统计和分析 fmt.Printf(" 应用1 - 成绩统计:\n") scores := [10]int{85, 92, 78, 96, 88, 73, 91, 87, 94, 82} // 计算总分和平均分 total := 0 for _, score := range scores { total += score } average := float64(total) / float64(len(scores)) // 找最高分和最低分 max, min := scores[0], scores[0] for _, score := range scores { if score > max { max = score } if score < min { min = score } } fmt.Printf(" 成绩: %v\n", scores) fmt.Printf(" 总分: %d\n", total) fmt.Printf(" 平均分: %.2f\n", average) fmt.Printf(" 最高分: %d\n", max) fmt.Printf(" 最低分: %d\n", min) // 应用2: 频率统计 fmt.Printf(" 应用2 - 字符频率统计:\n") text := "hello world" var frequency [26]int // 26个字母的频率 for _, char := range text { if char >= 'a' && char <= 'z' { frequency[char-'a']++ } } fmt.Printf(" 文本: \"%s\"\n", text) fmt.Printf(" 字母频率:\n") for i, count := range frequency { if count > 0 { fmt.Printf(" %c: %d\n", 'a'+i, count) } } // 应用3: 查找表 fmt.Printf(" 应用3 - 月份天数查找表:\n") daysInMonth := [12]int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} months := [12]string{ "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月", } for i := 0; i < len(months); i++ { fmt.Printf(" %s: %d天\n", months[i], daysInMonth[i]) } // 应用4: 缓冲区 fmt.Printf(" 应用4 - 固定大小缓冲区:\n") buffer := [8]byte{} data := []byte("Hello") // 将数据复制到缓冲区 copy(buffer[:], data) fmt.Printf(" 缓冲区: %v\n", buffer) fmt.Printf(" 缓冲区内容: %s\n", string(buffer[:len(data)])) // 应用5: 矩阵运算 fmt.Printf(" 应用5 - 矩阵加法:\n") matrixA := [2][2]int{{1, 2}, {3, 4}} matrixB := [2][2]int{{5, 6}, {7, 8}} var result [2][2]int // 矩阵加法 for i := 0; i < 2; i++ { for j := 0; j < 2; j++ { result[i][j] = matrixA[i][j] + matrixB[i][j] } } fmt.Printf(" 矩阵A: %v\n", matrixA) fmt.Printf(" 矩阵B: %v\n", matrixB) fmt.Printf(" A + B: %v\n", result) fmt.Println() } // demonstrateArrayVsSlice 演示数组 vs 切片 func demonstrateArrayVsSlice() { fmt.Println("7. 数组 vs 切片:") fmt.Printf(" 数组特点:\n") fmt.Printf(" - 固定长度,长度是类型的一部分\n") fmt.Printf(" - 值类型,赋值和传参会拷贝\n") fmt.Printf(" - 内存效率高,栈上分配\n") fmt.Printf(" - 编译时确定大小\n") fmt.Printf(" - 可以直接比较\n") fmt.Printf(" 切片特点:\n") fmt.Printf(" - 动态长度,可以增长和缩减\n") fmt.Printf(" - 引用类型,赋值和传参传递引用\n") fmt.Printf(" - 更灵活,但有额外开销\n") fmt.Printf(" - 运行时动态分配\n") fmt.Printf(" - 不能直接比较\n") // 性能比较示例 fmt.Printf(" 使用场景建议:\n") fmt.Printf(" 数组适用于:\n") fmt.Printf(" - 固定大小的数据集合\n") fmt.Printf(" - 性能敏感的场景\n") fmt.Printf(" - 需要值语义的场景\n") fmt.Printf(" - 小规模数据\n") fmt.Printf(" 切片适用于:\n") fmt.Printf(" - 动态大小的数据集合\n") fmt.Printf(" - 需要灵活操作的场景\n") fmt.Printf(" - 大规模数据处理\n") fmt.Printf(" - 函数参数传递\n") // 转换示例 fmt.Printf(" 数组和切片的转换:\n") arr := [5]int{1, 2, 3, 4, 5} slice := arr[:] // 数组转切片 fmt.Printf(" 原数组: %v\n", arr) fmt.Printf(" 转换的切片: %v\n", slice) // 修改切片会影响原数组 slice[0] = 99 fmt.Printf(" 修改切片后:\n") fmt.Printf(" 数组: %v\n", arr) fmt.Printf(" 切片: %v\n", slice) fmt.Println() } // ========== 辅助函数 ========== // 值传递数组(会拷贝整个数组) func modifyArrayByValue(arr [3]int) { arr[0] = 999 fmt.Printf(" 函数内修改: %v\n", arr) } // 指针传递数组(传递数组地址) func modifyArrayByPointer(arr *[3]int) { arr[0] = 888 fmt.Printf(" 函数内修改: %v\n", *arr) } /* 运行这个程序: go run 01-arrays.go 学习要点: 1. 数组是固定长度的元素序列,长度是类型的一部分 2. 数组是值类型,赋值和传参会拷贝整个数组 3. 数组的零值是所有元素都为零值的数组 4. 数组可以直接比较(相同类型和长度) 5. 多维数组本质上是数组的数组 数组的特性: 1. 固定长度:编译时确定,不能改变 2. 类型安全:所有元素必须是相同类型 3. 连续内存:元素在内存中连续存储 4. 索引访问:O(1) 时间复杂度 5. 值语义:赋值和传参会拷贝 声明和初始化方式: 1. var arr [n]Type 2. arr := [n]Type{values...} 3. arr := [...]Type{values...} // 自动推断长度 4. arr := [n]Type{index: value} // 指定索引 数组 vs 切片: 1. 数组:固定长度,值类型,性能高 2. 切片:动态长度,引用类型,更灵活 使用建议: 1. 小规模、固定大小的数据使用数组 2. 需要动态调整大小时使用切片 3. 性能敏感的场景考虑数组 4. 函数参数通常使用切片而不是数组 常见应用场景: 1. 固定大小的缓冲区 2. 查找表和映射表 3. 矩阵和多维数据 4. 统计和频率分析 5. 游戏棋盘等固定结构 注意事项: 1. 数组长度是类型的一部分 2. 不同长度的数组是不同类型 3. 数组传参会拷贝,大数组考虑传指针 4. 数组索引越界会 panic 5. 多维数组的内存布局是行优先的 */