Go中应优先使用切片而非数组,因切片可动态伸缩且复制开销小;数组长度固定、值语义传递,仅适用于栈上小尺寸场景。

数组声明后长度不可变,切片才是日常主力
Go 里 [5]int 是数组,长度写死在类型里;[]int 才是切片,底层指向底层数组、可动态伸缩。绝大多数场景该用切片,不是数组——除非你明确需要栈上固定大小、值语义传递(比如作为 map key 或结构体字段且尺寸极小)。
常见误操作:var a [3]int; b := a 复制的是整个数组(9 字节拷贝),而 s := []int{1,2,3}; t := s 复制的只是切片头(24 字节指针+长度+容量),底层数组仍共享。
用 make 创建切片时,len 和 cap 区别直接影响扩容行为
make([]int, 3) 创建 len=3、cap=3 的切片;make([]int, 3, 10) 创建 len=3、cap=10 的切片。后者在追加不超过 7 个元素时不会触发底层数组重新分配,避免频繁内存拷贝。
- append 超过 cap 时,Go 默认按约 2 倍扩容(小容量)或 1.25 倍(大容量),新底层数组地址会变,原切片和新切片不再共享数据
- 若已知最终大小,优先用
make([]T, 0, expectedCap)+append,比反复 append 更高效 - 检查是否发生扩容:比较
&s[0]和&newS[0]地址是否相等
切片截取不复制数据,但可能意外延长底层数组生命周期
s := make([]int, 1000); small := s[:1] —— small 只用第一个元素,但整个容量为 1000 的底层数组只要 small 还存活,就无法被 GC 回收。
立即学习“go语言免费学习笔记(深入)”;
这在函数返回局部大数组的子切片时尤其危险:
func bad() []byte {
big := make([]byte, 1e6)
return big[:10] // 千万字节数组因这 10 字节切片而驻留内存
}
修复方式:显式拷贝 return append([]byte(nil), big[:10]...),或用 copy 到新切片。
遍历切片优先用 for range,但注意索引复用和值拷贝陷阱
for i, v := range s 中,v 是每个元素的副本(对大结构体代价高),i 是重用的同一个变量地址。典型错误:
vals := []string{"a", "b", "c"}
var pointers []*string
for _, v := range vals {
pointers = append(pointers, &v) // 全部指向最后一个 v 的地址!
}
正确写法:pointers = append(pointers, &vals[i]),或在循环内声明新变量:v := v; pointers = append(pointers, &v)。
若只需索引,用 for i := range s;若需原地修改,直接用 s[i] = ...,避免无谓的 v 副本。










