Go中函数传参均为值传递:array传整个数据块,修改不影响原值;slice传ptr/len/cap结构体副本,改元素生效但append扩容无效;需修改slice本身时必须传*[]T;array可作map key而slice不可;调试用%p打印&s[0]验证底层数组共享。

函数传参时,array改不动原值,slice能改但扩容无效
Go 里没有真正的“引用传递”,全是值传递——但 array 和 slice 的“值”含义完全不同,这直接决定你在函数里能不能改到外面的数据。
-
array是完整数据块,传参拷贝整个内存(比如[1000]int就拷 8KB),函数内任何修改都只作用于副本 -
slice传的是一个三元结构体(ptr/len/cap)的拷贝,其中ptr指向原底层数组,所以改元素会生效;但append后若超出cap,会分配新数组、更新ptr—— 这个新ptr只存在函数内,调用方变量仍指向旧地址 - 常见错误现象:
append(s, x)后原 slice 长度没变、数据也没新增,还以为函数没执行成功
什么时候必须传 *[]T 而不是 []T
只有当你需要让调用方变量本身(不只是它指向的数据)被替换时,才需要指针。这不是“更安全”或“更标准”,而是语义必需。
- 典型场景:封装一个可增长的构建器函数,比如
func PushInts(s *[]int, vals ...int),内部必须写*s = append(*s, vals...) - 误用代价高:传
[]int却在函数里反复append,结果每次扩容都产生新底层数组,旧数组因仍有 slice 引用而无法回收 → 内存泄漏风险 - 性能上其实没优势:
[]int拷贝 24 字节,*[]int传 8 字节地址,但多了 nil 判断和一次解引用,还可能引入竞态(并发读写同一底层数组)
array 能做 map key,slice 不行 —— 为什么这很重要
因为 array 是可比较类型(固定长度 + 所有元素可比),slice 不是。这个区别常被忽略,却直接影响数据结构选型。
- 想用「固定长度的坐标」做缓存 key?
[2]int可以,[]int直接报错:invalid map key type []int - 网络协议或二进制格式中需精确内存布局?
[16]byte表示 UUID 安全可靠;换成[]byte就失去可比性、不可哈希、也无法保证连续性 - 小数据量且结构稳定时,
array反而更轻量——无指针、无 cap 管理、GC 压力为零
调试时怎么看底层数组是否共享?用 fmt.Printf("%p", &s[0])
别猜,直接打印首元素地址。这是最简单也最可靠的验证方式。
立即学习“go语言免费学习笔记(深入)”;
- 两个
slice指向同一底层数组?它们的&s[0]地址相同(注意不是s本身地址) - 扩容后地址变了?说明
append分配了新数组,原 slice 已断开联系 - 对
array做切片得到slice,再打印&arr[0]和&s[0],通常一致 —— 这就是“切片是数组视图”的实证










