数组是固定长度值类型,赋值复制全部元素;切片是动态长度引用类型,仅复制指针、len、cap,底层数组共享。声明上[3]int为数组,[]int为切片。

数组和切片在 Go 里根本不是同一类东西:一个是固定尺寸的内存块,另一个是带元信息的动态视图。选错会导致静默 bug、性能浪费或意外共享。
怎么一眼区分 [3]int 和 []int
看声明语法是最直接的方式:[3]int 是数组,方括号里有具体数字;[]int 是切片,方括号空着。但更关键的是行为差异:
-
[3]int是值类型——赋值或传参时整块复制,改副本不影响原变量 -
[]int是引用类型——复制只拷贝指针、len、cap这三个字段,底层数组仍共用 - 即使写
s := arr[0:3](从数组截取),得到的仍是切片,不是数组
为什么 append 后原切片有时“失效”
因为 append 可能触发扩容:当当前容量 cap 不足时,运行时会分配新底层数组、复制旧数据、返回指向新地址的切片。原变量若没接收返回值,就还在旧底层数组上,后续修改可能被覆盖或丢失。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 扩容策略:长度
- 判断是否扩容:比较
len(s)和cap(s),相等即下次append必扩容 - 避免意外:总是用
s = append(s, x),别忽略返回值
函数传参时 []int 和 [3]int 的实际表现
传 [3]int 进函数,函数内修改不会影响调用方——因为传的是完整副本;而传 []int,函数内通过索引修改元素,调用方看到的就是改后的值,因为底层数据是同一块内存。
- 常见陷阱:把切片传进函数做排序或过滤,却忘了它可能修改原数据
- 想隔离修改?要么传
append([]int(nil), s...)拷贝一份,要么在函数内用make新建底层数组 - 数组作参数类型严格:
func f(a [3]int)和func f(a [4]int)是两个不同函数签名
什么时候非用数组不可
绝大多数业务逻辑用切片就够了。数组只在极少数场景下有不可替代性:
- 需要作为
map的键(切片不能做 key,数组可以,如map[[3]byte]string) - 明确要求固定长度且禁止越界/扩容,比如协议头、硬件寄存器映射、RGB 像素
[3]uint8 - 极致性能压榨:小数组(如
[4]int)常驻栈上,无堆分配开销;而切片总要维护指针和元数据 - 用作切片的底层存储(比如
var buf [64]byte; s := buf[:0]避免频繁 malloc)
真正容易被忽略的点是:**切片的“动态”不等于安全——共享底层数组、扩容时机不确定、append 返回新切片,这三者叠加,是 Go 中最常引发隐蔽并发或逻辑错误的组合。**
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










