Go数组传参默认是完整拷贝,函数内修改不影响原数组;必须用*[N]T指针才能修改原数组,小数组可值传,大数组应优先用切片或指针。

Go数组传参默认是完整拷贝,不是共享底层数组
Go里[N]T(固定长度数组)作为参数传入函数时,会复制整个数组内容,函数内修改元素不影响原数组。这不是“引用行为”,也不是“切片那种伪引用”,就是实打实的内存拷贝。
常见错误现象:传[3]int进函数,改了第一个元素,main里一打印还是老值——不是bug,是设计如此。
- 数组大小决定开销:小数组(如
[4]byte)拷贝快;大数组(如[1000000]int)会卡顿、占内存 - 地址完全隔离:函数内
&arr和调用方&original地址不同,fmt.Printf("%p", &arr)能直接验证 - 不支持“扩容”或“重切”:数组长度固定,
arr = [3]int{9,9,9}只是改副本,对外零影响
想原地修改数组,必须传指针*[N]T
只有显式传*[N]T,才能让函数操作原始内存。这时传的是8字节(64位系统)地址副本,解引用后写入即生效。
使用场景:需要批量初始化、加密填充、硬件寄存器映射等必须改原数组的场合。
立即学习“go语言免费学习笔记(深入)”;
- 语法要带括号:
func fill(ptr *[4]int),不能写成func fill(ptr *[4]int)(这个对)或func fill(ptr *[4]int)(错,Go不认) - 调用时必须取地址:
fill(&myArray),漏掉&会编译报错cannot use myArray (type [4]int) as type *[4]int - 性能对比:传
[1024]byte拷贝1KB;传*[1024]byte只拷贝8字节指针
[N]T和[]T传参行为完全不同,别混用
新手常把数组和切片当一回事,但它们底层结构天差地别:[N]T是值,[]T是含指针的结构体。传[]T能改元素是因为拷贝了ptr字段;传[N]T连ptr都没有,纯数据块。
典型误用:定义var a [3]int,却想用append(a[:], 4)扩展——不行。a[:]生成切片,但a本身没变,且append返回新切片,原数组长度永远锁死为3。
- 转换成本明确:
a[:]不拷贝数据,只构造新slice header(3个字段),开销可忽略 - 但反向不可逆:从
[]T无法安全转回[N]T,除非len(s) == N && cap(s) >= N且你手动copy - 接口接收时更易踩坑:
func f(v interface{})传[3]int会完整拷贝;传[]int只拷贝24字节header
什么时候该用数组而不是切片?看是否需要栈上确定布局
数组的核心价值不在“传参”,而在编译期长度已知、内存连续、可做结构体字段或map key。传参只是附带行为。
容易被忽略的点:struct里嵌[32]byte比嵌[]byte更省内存(无额外header)、无GC压力;map[[16]byte]int合法,map[[]byte]int编译失败。
- 小数组(≤8字节)按值传没问题,比如
[2]int、[8]byte - 中等数组(如
[64]byte)若频繁传参,考虑*[64]byte或改用[]byte+make - 大数组(≥1KB)几乎总是该用
[]byte或*[N]byte,否则编译器可能警告large stack frame


















