传大数组必须用[N]T指针,避免值传递导致的8MB拷贝;函数签名需明确长度如[1000000]int,调用时用&arr取址,禁止对字面量取址,range ptr可高效遍历原数组。

传大数组必须用 *[N]T,别用值传递
Go 中数组是值类型,[1e6]int 传参时会拷贝整整 8MB(假设 int 是 8 字节),函数返回后原数组不变,还拖慢执行、加重 GC。要改原数组且保证零拷贝,唯一正解是传 *[N]T 指针。
- 函数签名必须明确长度:
func process(ptr *[1000000]int),不能写*[]int或[]int - 调用时必须用
&arr取地址,&[3]int{1,2,3}是非法语法,编译直接报错 - 函数内可直接用
ptr[i](等价于(*ptr)[i]),无需每次都写解引用 -
range ptr合法且高效——Go 会自动解引用并遍历原数组,不是遍历指针本身
想单独控制每个元素地址?用 []*T 切片,不是 *[N]T
*[N]T 是“指向整个数组的指针”,而你真正需要“每个元素各自可变的地址”时,得用 []*T(指针切片)或 [N]*T(固定长度指针数组)。两者用途完全不同。
- 优先选
[]*T:灵活、可扩容,适合处理结构体切片或条件更新场景 - 初始化必须基于**已命名变量**:
nums := [3]int{1,2,3}; ptrs := []*int{&nums[0], &nums[1], &nums[2]} - 禁止对字面量取址:
&[]int{1,2,3}[0]或&[3]int{1,2,3}[1]全部编译失败 - 循环中别写
for _, v := range nums { p := &v }——v是副本,所有p都指向同一个栈地址
修改原数组元素,关键看地址是否有效
只要指针指向的是原数组元素的真实地址,*p = x 就一定生效。但“地址是否还有效”,取决于数组生命周期。
- 全局变量、包级变量、
make([]T, n)底层数组、结构体字段里的数组 → 地址长期有效 - 局部数组变量(如函数内
arr := [3]int{})→ 若返回其元素地址(如return &arr[0]),函数退出后该地址可能被复用,解引用会 panic 或读到垃圾值 - 用
go tool compile -gcflags="-m"查逃逸分析:若提示... moves to heap,说明编译器已帮你挪到堆上,安全 - 结构体指针数组(如
[]*Person)最常用也最安全,因为&Person{}本就会逃逸到堆
切片 []T 和数组指针 *[N]T 别混用
它们底层都含指针,但语义、约束和行为差异极大。误当同一种东西用,轻则编译失败,重则逻辑错误。
立即学习“go语言免费学习笔记(深入)”;
-
[]T是三元结构(指针+长度+容量),可append、可切片;*[N]T是纯地址,长度在类型里固化,len(*ptr)编译期就确定,无法扩容 - 传
*[5]int的函数,不能接收&[3]int{}或&someSlice——类型不兼容,编译报错cannot use ... as *[5]int - 想从
*[N]T转切片?写(*ptr)[:]即可;但反向转换([]T→*[N]T)几乎不可能,除非你 100% 确认底层数组长度且可寻址 - 日常开发优先用切片;只有当你需要编译期长度检查、对接 C、或处理超大固定尺寸缓冲区时,才显式用
*[N]T
最易被忽略的一点:数组指针的“安全性”来自它的不可变性——*[100]int 永远只能操作那 100 个位置,不会越界也不会扩容;而切片的灵活性恰恰是双刃剑,append 可能悄悄换掉底层数组,导致你以为在改原数据,其实没动。










