示例工程教程【免费下载链接】learngo❤️ 1000 Hand-Crafted Go Examples, Exercises, and Quizzes. Learn Go by fixing 1000 tiny programs.项目地址https://gitcode.com/gh_mirrors/le/learngo点击查看免费下载切片与数组的取舍是每个 Go 开发者入门时绕不开的第一个分水岭问题。本文以 learngo 仓库 16-slices/questions/1-slices-vs-arrays.md 这一经典测验文档为主线逐题拆解 10 个关键知识点——从为什么用切片到为什么切片不能比较并对照仓库中的演示源码与练习讲透二者的底层差异。读完后你将能准确判断一个集合该用数组还是切片、能解释 nil 切片的行为边界并理解动态长度在运行时而非编译期意味着什么。一图看懂数组与切片的本质区别在逐题拆解之前先建立一个总览框架。两者最核心的区别可以用一句话概括数组的长度是类型的一部分切片则不是。这一点决定了后续所有行为差异。维度数组Array切片Slice长度是否属于类型是[5]int与[3]int是不同类型否[]int与[]int永远是同一类型长度变化时机编译期固定不可变运行时可变动态集合零值长度确定的零值元素集合nil可比较性可以用比较只能与nil比较是否可动态增删元素不可借助append动态增长这一总览可以从仓库演示源码得到直接印证16-slices/02-slices-vs-arrays/main.go 中同时打印了数组与切片的类型和零值行为var array [2]int fmt.Printf(array : %#v\n, array) // [2]int{0, 0}零值元素 var slice []int fmt.Println(slice nil?, slice nil) // true零值是 nil fmt.Println(len(slice) :, len(slice)) // 0len 依然可用 fmt.Printf(arrays type: %T\n, array) // [2]int fmt.Printf(slices type: %T\n, slice) // []int第 1 题为什么你要用切片而不是数组原题Why you want to use a slice instead of an array? 正确答案A slices length is dynamic, so I can create dynamic collections切片长度是动态的可以创建动态集合数组的长度在声明时就已经确定并写进了类型里例如[5]int永远只能容纳 5 个元素而切片是对底层数组的一段视图长度可以在运行时通过append增长。这意味着编译期你不需要预先知道数据规模运行时可以按需扩容、收缩同一个切片变量可以被重新赋值成不同长度的切片。仓库 16-slices/04-slices-vs-arrays-unique-nums/02-with-slices/main.go 给出了一个极具说服力的实战案例生成若干互不重复的随机数。它从一个未初始化的var uniques []intnil 切片开始循环中使用append动态累积元素直到长度达到目标maxvar uniques []int loop: for len(uniques) max { // len 用于 nil 切片同样有效 n : rand.Intn(max) 1 for _, u : range uniques { if u n { continue loop } } uniques append(uniques, n) // 动态追加 } fmt.Println(length of uniques:, len(uniques)) sort.Ints(uniques) // 排序后打印uniques的最终长度在程序运行时才确定这正是动态集合的含义。对照同目录下01-with-arrays的实现需要found计数器配合手动赋值可以直观感受切片的优势。第 2 题切片的长度属于编译期还是运行时原题Where does the length of a slice belong to? 正确答案Runtime运行时错误选项里的 Compile-Time编译期、Walk-Time、Sleep-Time 都是干扰项。原文档给出了解析要点A slices length is not a part of its type. So its length can change at runtime.切片的长度不属于它的类型所以它能在运行时改变。这句话正是切片与数组的分水岭。数组[5]int的长度 5 被编码在类型系统中编译期就固定下来而切片类型是[]int不携带任何长度信息任何[]int值长度 0、5、1000都满足该类型。因此函数签名里写[]int就能接收任意长度的切片赋值时可以把任意长度的切片赋给同一个变量长度的合法性在运行时由len、append、索引边界等机制检查。仓库 16-slices/01-slices-vs-arrays/main.go 用并排对比代码展示了这一点注释中明确写着数组its length is part of its type切片its length is not part of its type。第 3 题函数调用中的类型精确匹配原题以下哪个函数调用是正确的给定函数func sort(nums []int) { ... }选项sort([...]int{3, 1, 6})—— 错误不能用数组调用它期望的是 int 切片sort([]int32{3, 1, 6})—— 错误不能用 int32 切片调用它期望的是 int 切片sort([]int{3, 1, 6})——正确这道题揭示 Go 严格的静态类型特性[]int、[3]int、[]int32是三种完全不同的类型函数参数必须精确匹配Go 不会做隐式转换。数组 ≠ 切片[...]int{3, 1, 6}是数组类型[3]int不能传给要求[]int的参数。若确实要用数组需要显式切片sort(nums[:])——这正是 16-slices/04-slices-vs-arrays-unique-nums/02-with-slices/main.go 中sort.Ints(nums[:])的用法它对[5]int数组做了一次切片表达式得到[]int。元素类型也须一致[]int32与[]int的元素类型不同同样无法互传。即使int32和int底层都是整数Go 也不允许隐式转换。第 4 题切片的零值是 nil原题var tasks []string的零值是什么 正确答案nil错误选项0、1、unknown原文档解析This is a nil slice. Unlike an array, a slices zero value is nil.这是 nil 切片。与数组不同切片的零值是 nil。一个只声明未初始化的切片变量值为nil表示它还没有指向任何底层数组。对比记忆数组的零值是长度确定、每个元素为零值的集合例如[2]int的零值是[0 0]切片的零值是nil。仓库 16-slices/exercises/01-declare-nil/solution/main.go 演示了各种元素类型的 nil 切片声明并统一打印类型、长度与是否为 nilvar ( names []string // The names of your friends distances []int // The distances data []byte // A data buffer ratios []float64 // Currency exchange ratios alives []bool // Up/Down status of web servers ) fmt.Printf(names : %T %d %t\n, names, len(names), names nil)无论元素类型是 string、int、byte、float64 还是 bool未初始化的切片一律是nillen均为 0与nil比较均为true。第 5 题nil 切片上使用 len 会怎样原题var tasks []string后执行fmt.Println(len(tasks))输出什么 正确答案0原文档解析Yes, you can use the len function on a nil slice. It returns 0 because the slice doesnt contain any elements yet.是的你可以对 nil 切片使用 len 函数它返回 0因为切片还不包含任何元素。这是切片非常友好的特性len(nil_slice)合法且返回 0。因此你总是可以放心地对一个可能为 nil 的切片调用len、range等操作无需先做 nil 判断。这在 16-slices/02-slices-vs-arrays/main.go 中有直接演示len(slice): 016-slices/04-slices-vs-arrays-unique-nums/02-with-slices/main.go 中for len(uniques) max的循环条件也依赖这一点——初始的 nil 切片 len 为 0循环得以启动。第 6 题nil 切片上做索引会怎样原题var tasks []string后执行fmt.Println(tasks[0])输出什么 正确答案It doesnt work.程序会报错错误选项0、1、nil原文档解析You cant get an element that does not exist. A nil slice does not contain any elements.你不能取一个不存在的元素nil 切片不包含任何元素。这是上一题的硬币另一面虽然len对 nil 切片宽容地返回 0但索引越界会触发运行时 panic。tasks[0]试图访问不存在的第 1 个元素Go 运行时立即报错panic: runtime error: index out of range [0] with length 0仓库 16-slices/01-slices-vs-arrays/main.go 中特意用注释保留了这两行会崩的代码提醒学习者// wont work: the slice is nil. // fmt.Printf(nums[0]: %d\n, nums[0]) // fmt.Printf(nums[1]: %d\n, nums[1])实际开发中安全读取切片的惯用写法是先判断len(slice) 0或索引前检查i len(slice)。第 7 题哪种写法才是正确的切片声明原题Which declaration below is a correct slice declaration? 选项[...]int{}—— 这是数组省略号让编译器按元素个数推断长度不是切片[2]string{hello, world}—— 这是长度为 2 的数组[]string{hello, world}——正确方括号内没有长度就是切片字面量string[2]{hello, world}—— 语法错误类型后置、world缺引号且 Go 中不存在这种写法。判定数组与切片的一眼技巧就是看方括号里有没有长度或...[...]T{}、[N]T{}→ 数组[]T{}→ 切片。仓库 16-slices/exercises/04-declare-arrays-as-slices/solution/main.go 用一组切片字面量展示了各元素类型的标准写法names : []string{Einstein, Tesla, Shepard} distances : []int{50, 40, 75, 30, 125} data : []byte{H, E, L, L, O} ratios : []float64{3.14145} alives : []bool{true, false, true, false} zero : []byte{}注意最后的zero : []byte{}这是一个非 nil 的空切片长度 0 但有底层数组与var zero []byte的 nil 切片不同但二者的len都是 0。这一细微差别会在后续关于切片内部的测验中进一步展开。第 8 题为什么切片不能用 比较原题下面的代码为什么编译不过colors : []string{red, blue, green} tones : []string{dark, light} if colors tones { ... }正确答案Slices cannot be compared切片不能被比较原文档解析Thats right! A slice value can only be compared to a nil value.切片值只能与 nil 比较。这是切片与数组又一个重要区别数组可以用逐元素比较切片不行Go 1.21 之前唯一合法的是slice nil这种与 nil 的比较切片之间比较是非法的编译错误。根本原因与切片的内部结构有关切片由指针 长度 容量三部分组成它指向底层数组的一段两个切片即使元素相同也可能指向不同底层数组语义上难以定义相等此外需要类型可哈希而切片可变。因此 Go 直接禁止切片之间用。那如何判断两个切片内容是否相等标准做法是手动逐元素比较并先比较长度。仓库 16-slices/03-slices-vs-arrays-examples/main.go 演示了完整的切片相等性判断模式games : []string{pokemon, sims} newGames : []string{pacman, doom, pong} newGames games // 切片可以整体赋值指向同一底层数组 games nil // 显式置为 nil games []string{} // 空切片字面量 var ok string for i, game : range games { if game ! newGames[i] { // 逐元素比较 ok not break } } if len(games) ! len(newGames) { // 先比较长度 ok not } fmt.Printf(games and newGames are %sequal\n, ok)同一文件中数组books与newBooks则直接使用if books newBooks并在长度相同的前提下用books newBooks整体赋值——这正是数组与切片比较行为的直观对照。第 9 题空切片字面量的长度是多少原题[]uint64{}的长度是多少 正确答案0错误选项64、1、Error原文档解析Thats right. This is an empty slice, it doesnt contain any elements.这是空切片不包含任何元素。这道题的干扰项设计得很巧妙uint64中的 64 是位数8 字节与元素个数无关[]uint64{}花括号内没有任何元素所以len返回 0。切片字面量的长度就是花括号中实际列出的元素个数与元素类型完全无关。第 10 题切片字面量的长度如何计算原题[]string{Im, going, to, stay, \here\}的长度是多少 正确答案5花括号里共有 5 个字符串元素Im—— 单引号是普通字符无需转义goingtostay\here\—— 字符串值为here含转义的双引号它仍然只是一个元素。注意两点字符串内部的转义\只影响字符串内容不影响元素个数长度计数的是顶层元素个数字符串本身再长也只是一个元素。所以len的结果是 5。这道题提醒初学者计算切片长度时把花括号里的元素逐一数清即可不要被元素内容的复杂格式干扰。从源码看切片背后的动态能力看完 10 道题再回到仓库源码你会发现切片的所有便利都围绕一个主题把长度从类型中解放出来交给运行时管理。几个可以继续深入探索的证据链动态增长依赖appendnil/空切片都没有现成元素可用增长只能通过append完成且必须接收其返回值nums append(nums, 4)因为append可能返回新的底层数组。演示见 16-slices/05-append/1-theory/main.go其中_ append(nums, 4)与nums append(nums, 4)的对比尤其值得注意——前者丢弃返回值切片长度不会改变。数组可以一键切片化需要把数组传给要求切片参数的函数时用切片表达式nums[:]即可见 16-slices/04-slices-vs-arrays-unique-nums/02-with-slices/main.go 中sort.Ints(nums[:])。切片相等性需手动实现因为切片不能直接比较通用模式是先比长度、再逐元素比参考 16-slices/03-slices-vs-arrays-examples/main.go。配套练习仓库 16-slices/exercises 中的01-declare-nil声明各种 nil 切片、02-empty空切片 vs nil 切片、04-declare-arrays-as-slices数组字面量改写为切片字面量等练习都是对这 10 道题知识点的动手巩固。此外16-slices/README.md 提到该章节的部分演示代码如 05-append 的1-theory依赖github.com/inancgumus/prettyslice库来可视化切片内部结构可通过go get -u github.com/inancgumus/prettyslice安装——它能把底层数组、长度、容量渲染成表格是直观观察长度不属于类型这一特性的利器。更多测验主题append、slicing、backing array、capacity 等见 16-slices/questions/README.md。小结数组长度是类型的一部分编译期固定切片长度不属于类型运行时可变——这是全部差异的总根源未初始化的切片零值为nillen返回 0但索引会 panic切片只能与nil比较不能与另一个切片比较判断内容相等需先比长度再逐元素比函数参数要求精确的类型匹配[]int与[3]int、[]int32互不兼容日常开发中动态集合优先选切片需要固定大小、可整体比较的场合才使用数组。把这 10 道题连同仓库源码和练习全部跑通你就掌握了 Go 集合类型中最重要的一个判断什么时候选切片、为什么以及它的边界在哪里。赞分享示例工程教程【免费下载链接】learngo❤️ 1000 Hand-Crafted Go Examples, Exercises, and Quizzes. Learn Go by fixing 1000 tiny programs.项目地址https://gitcode.com/gh_mirrors/le/learngo点击查看免费下载相关推荐LunaTV网盘搜索与ACG种子搜索PanSou与Mikan Project双源系统终极指南LunaTV网盘搜索与ACG种子搜索PanSou与Mikan Project双源系统终极指南 欢迎来到LunaTV增强版的全功能影视聚合平台 在这篇完整MoviePy初探Python视频剪辑自动化终极指南为什么它是程序员的剪辑神器MoviePy初探Python视频剪辑自动化终极指南为什么它是程序员的剪辑神器 MoviePy 是一个用 Python 做视频剪辑的开源库让视频剪辑自动化示例工程教程BepInEx IL2CPP 启动失败排查黑窗闪退到正常进游戏的 3 条最短路径BepInEx IL2CPP 启动失败排查黑窗闪退到正常进游戏的 3 条最短路径 游戏刚启动控制台黑窗一闪而过进程眨眼就没了可你把 BepInEx 文件游戏开发插件系统上一篇xi-editor 的 CRDT 并发编辑与撤销模型从《Rope Science 8a》看中央式 CRDT 的设计取舍下一篇Podman --logfile 选项深度解析将 podman build 与 farm build 的构建日志重定向到文件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考