切片是包含len、cap和底层数组指针的结构体,声明为[]t;数组是固定长度[n]t,二者类型不兼容。var s []int为nil切片,var a [3]int为长度3的数组,a[:]可转为切片。

怎么声明一个切片,和数组有什么本质区别
切片不是数组的别名,而是包含 len、cap 和指向底层数组的指针的结构体。声明时用 []T(没数字),而数组是 [N]T(有固定数字)。
-
var s []int声明了一个 nil 切片,len(s)和cap(s)都是 0,但s == nil成立 -
var a [3]int是长度为 3 的数组,占固定内存,不能直接传给期望[]int的函数 - 把数组转成切片要用
a[:],不是a;传参时常见错误是把[3]int当作[]int直接传,会编译报错:cannot use a (type [3]int) as type []int in argument
make 创建切片时,len 和 cap 参数怎么选
make([]T, len, cap) 中,len 是初始长度(可访问元素个数),cap 是底层数组总容量(决定后续 append 是否触发扩容)。
- 如果只写
make([]int, 5),等价于make([]int, 5, 5),cap默认等于len - 如果预估后续要追加很多元素,比如最终大概要 100 个,建议写
make([]int, 0, 100):避免多次 realloc 和复制,提升性能 -
cap小于len会编译失败,这是语法错误,不是运行时 panic -
cap超过底层数组实际大小也没问题——因为make自己分配内存,你只是在定义逻辑上限
append 导致切片“意外共享”数据怎么办
append 在容量足够时不分配新内存,直接复用底层数组;如果多个切片共用同一底层数组,改一个可能影响另一个。
- 常见现象:循环中反复
append同一个切片,结果所有子切片内容“同步变化” - 根本原因:没有做深拷贝,比如
newSlice := oldSlice[:]还是共享底层数组 - 安全做法是显式复制:
newSlice := append([]int(nil), oldSlice...)或newSlice := make([]int, len(oldSlice)); copy(newSlice, oldSlice) - 注意
append([]int{}, s...)看似简洁,但每次调用都新建底层数组,适合小数据;大数据量下copy更可控
切片截取越界 panic 怎么快速定位
panic 错误信息是:panic: runtime error: slice bounds out of range [:n] with length m,其中 n 是你请求的结束索引,m 是当前长度。
立即学习“go语言免费学习笔记(深入)”;
- 截取写法
s[i:j:k]要同时满足:0 ≤ i ≤ j ≤ k ≤ cap(s);漏掉k时默认是cap(s) - 容易踩坑的是用
s[i:]时,以为i超过len(s)会返回空切片,其实只要i > len(s)就 panic(哪怕i ≤ cap(s)) - 调试建议:在截取前加一行
if i > len(s) { panic("index out of len") },比靠 panic 堆栈更早暴露逻辑错误 -
len(s)和cap(s)不相等时,尤其要注意s[:]和s[0:len(s)]行为一致,但s[0:cap(s)]可能越界
切片的“动态”不体现在语法上,而在于你对 len/cap 关系的理解是否准确;多数 bug 都藏在扩容时机和底层数组复用这两个点里。










