Golang中的切片与数组的区别_Golang切片与数组的深入对比分析

2次阅读

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

Golang中的切片与数组的区别_Golang切片与数组的深入对比分析

数组赋值会拷贝全部数据,切片赋值只拷贝头信息

这是最常被忽视的性能与行为差异。数组是值类型,arr2 := arr1 会把整个底层数组内容复制一遍;而切片是引用类型,slice2 := slice1 只复制三个字段:指针、lencap,两者仍指向同一底层数组。

  • 修改 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 指向一个零长底层数组)。两者 lencap 都为 0,且都能安全传给 appendlencap

立即学习go语言免费学习笔记(深入)”;

  • 区别仅在序列化(如 json.Marshal):nil 切片输出 NULL,空切片输出 []
  • if s == nil 只对真正 nil 切片成立;if len(s) == 0 覆盖所有空状态,更稳妥
  • 新建切片优先用 make([]T, 0, hint):既避免 nil panic(极少数函数不接受 nil),又预设容量防频繁扩容

底层共享和扩容时机这两个点,实际 debug 时最容易卡住——不是语法错,而是数据在某个 append 后突然“不联动”了,或两个本该隔离的变量悄悄改了对方的值。盯住 cap 和是否 nil,比死记规则管用。

text=ZqhQzanResources