数组赋值拷贝全部数据,切片赋值仅拷贝头信息(指针、len、cap);数组是值类型,切片是引用类型;传参时大数组应改用切片或指针以避免性能损耗。

数组赋值会拷贝全部数据,切片赋值只拷贝头信息
这是最常被忽视的性能与行为差异。数组是值类型,arr2 := arr1 会把整个底层数组内容复制一遍;而切片是引用类型,slice2 := slice1 只复制三个字段:指针、len、cap,两者仍指向同一底层数组。
- 修改
slice2[0]会影响slice1[0];但修改arr2[0]对arr1完全无影响 - 传参时同理:
func f(a [1000]int)每次调用都拷贝 1000 个 int;func f(s []int)始终只传 24 字节(64 位系统下) - 小数组(如
[3]float64)传值开销小,可接受;大数组务必改用切片或显式传指针*[1000]int
append 可能“断开”原底层数组,引发意外交互
append 不是总在原数组上追加——当容量不足时,它会分配新底层数组、复制旧数据、返回新切片。此时原切片和其他共享该底层数组的切片不再互通。
- 现象:
s1 := []int{1,2}; s2 := s1[0:2]; s1 = append(s1, 3); fmt.Println(s2)输出[1 2](没变),但若改成append(s1, 3, 4)(超出 cap),s2仍安全;可一旦s1扩容后又截取新切片,就可能误读旧数据 - 关键点:扩容是否发生,取决于当前
cap,而非len;可用cap(s) >= len(s)+n预判 - 避免踩坑:对需长期共用底层数组的场景(如批量解析 buffer),用
make([]byte, 0, knownSize)预分配足够cap,减少意外扩容
函数参数写 []T 还是 [N]T?看语义,不看长度
Go 编译器把 [5]int 和 [10]int 当作完全不同的类型,哪怕都只存 3 个数。而 []int 是通用类型,适配任意长度。
- 如果你的函数逻辑依赖“恰好 N 个元素”,比如矩阵行、固定协议头,用
[N]T—— 类型系统会强制校验 - 如果你要处理“一串 T”,不管几个,一律用
[]T;试图传[5]int给接收[]int的函数是合法的(自动转成切片),但反过来不行 - 常见错误:
var a [3]int; f(a[:])正确;f(a)报错:cannot use a (variable of type [3]int) as []int value in argument to f
nil 切片和空切片不是一回事,但多数情况可互换
var s []int 是 nil 切片(len=0, cap=0, data=nil);s := []int{} 或 s := make([]int, 0) 是非 nil 空切片(data 指向一个零长底层数组)。两者 len 和 cap 都为 0,且都能安全传给 append、len、cap。
立即学习“go语言免费学习笔记(深入)”;
- 区别仅在序列化(如
json.Marshal):nil 切片输出null,空切片输出[] -
if s == nil只对真正 nil 切片成立;if len(s) == 0覆盖所有空状态,更稳妥 - 新建切片优先用
make([]T, 0, hint):既避免 nil panic(极少数函数不接受 nil),又预设容量防频繁扩容
append 后突然“不联动”了,或两个本该隔离的变量悄悄改了对方的值。盯住 cap 和是否 nil,比死记规则管用。










