数组是值类型,切片是含指针、len、cap的结构体;赋值和传参时数组复制全部元素,切片仅复制24字节结构体;append可能扩容导致底层数组更换,影响共享数据。
数组是值类型,切片只是三个字段的结构体
声明 var a [3]int 时,Go 直接在栈上分配 3 个 int 的连续空间;而 s := []int{1,2,3} 实际只生成一个 24 字节的结构体(64 位系统下:8 字节指针 + 8 字节 len + 8 字节 cap),它不存数据,只“看”底层数组。
- 赋值
b := a→ 复制全部 3 个整数;t := s→ 只复制那 24 字节,s和t仍指向同一底层数组 - 传参时:
func f(a [1000]int)每次调用都压栈 1000 个整数;func g(s []int)只传 24 字节,且s[0] = 123会真实改到底层数组 - 零值差异:
var x [3]int是[0 0 0],可直接读写;var y []int是nil,len(y)为 0,y[0]会 panic
切片扩容时可能悄悄换掉底层数组
append 不一定复用原数组——是否扩容,取决于 len(s) == cap(s)。一旦触发扩容,Go 会分配新底层数组、拷贝旧数据、再追加,此时原切片和其他共享该底层数组的切片就“断连”了。
- 常见坑:
arr := [3]int{1,2,3}; s1 := arr[:2]; s2 := arr[1:3],然后s1 = append(s1, 99)→ 因cap(s1) == 3,未扩容,s2[0]突变为 99 - 但若
arr更长(如[5]int),s1的cap变成 5,append后不扩容,行为更隐蔽 - 扩容策略:
cap 时翻倍;cap >= 1024时增加 25%,实际申请容量还可能向上对齐
截取切片会共享底层数组,不是安全拷贝
a[1:3] 这类操作不会分配新内存,只是调整结构体里的 ptr 和 len,cap 也会变(从起点算到底层数组末尾)。这意味着修改子切片,可能意外污染原始数据。