*[3]int是指向含3个int的数组的指针,[3]*int是含3个int指针的数组;前者指向连续内存块,后者各指针可独立指向不同地址;类型不兼容,不可互赋。

怎么区分 *[3]int 和 [3]*int
这是最常混淆的两个类型,本质差在「* 修饰谁」:前者是指向数组的指针,后者是元素为指针的数组。
常见错误现象:cannot use &arr (type *[3]int) as type [3]*int in assignment —— 编译器直接报类型不匹配,不是值的问题,是类型定义层面就不兼容。
-
*[3]int:整个数组在内存里是一块连续区域,*指向这块区域起始地址;取地址用&arr,解引用后得到原数组*p -
[3]*int:数组本身存的是 3 个*int值,每个指针可指向不同位置的int,它们彼此独立 - 使用场景差异:需要把大数组传给函数又不想拷贝时,用
*[N]T;需要动态修改多个整数且允许某些为nil,用[N]*T
示例:
arr := [3]int{1, 2, 3}
p1 := &[3]int{1, 2, 3} // 类型是 *[3]int
p2 := [3]*int{&arr[0], &arr[1], &arr[2]} // 类型是 [3]*int
传参时该用 []int、*[3]int 还是 [3]*int
Go 函数参数不支持“按引用传递”,但可以用指针或切片绕过拷贝。选哪个,取决于你是否需要修改原数据、是否关心长度固定性。
立即学习“go语言免费学习笔记(深入)”;
- 用
[]int最常见:底层共享底层数组,修改元素会影响原 slice;但无法改变长度(除非重新 make) - 用
*[3]int:只在明确需要操作固定大小数组、且必须避免拷贝时才用;函数内可通过*p当作普通数组用,也可转成[]int:nums := (*p)[:] - 用
[3]*int:适合批量管理一组可为空的整数指针,比如配置项开关、可选字段;传参就是值传递,但每个*int仍指向原始变量 - 性能影响:
*[N]T传参只传 8 字节指针,[N]T是 N×size(T) 字节拷贝,[N]*T是 N×8 字节拷贝(指针本身)
new([3]int) 和 &[3]int{} 有什么区别
两者都返回 *[3]int,但初始化方式和语义不同,容易误以为等价。
-
new([3]int)分配零值数组内存,返回其地址;结果等价于&[3]int{0, 0, 0} -
&[3]int{}是复合字面量取地址,也分配零值,但它是显式构造——如果写成&[3]int{1},就变成[3]int{1, 0, 0},而new无法指定初始值 - 兼容性无差别,但可读性上
&[3]int{}更直观;new多用于泛型或类型名较长时(如new(MyVeryLongStruct)) - 注意:
new([3]int)返回的是指针,不是数组;不能对它做len或cap,得先解引用:len(*p)
为什么 [3]*int 不能直接用 range 修改原值
因为 range 遍历时,v 是每个 *int 的副本(即另一个指针),改 v 只是改了副本,不影响原数组里的指针值。
- 正确做法是用索引:
for i := range ptrs { *ptrs[i] = 42 } - 错误写法:
for _, v := range ptrs { *v = 42 }—— 看似一样,但v是副本,*v改的是它指向的值,没问题;但如果想让ptrs[0]指向新地址,v = &newVal就无效 - 容易踩的坑:误以为
range中的v是引用,尤其从 Python/JS 转过来的人;Go 里所有传参都是值传递,包括指针变量本身 - 验证方式:打印
&v和&ptrs[i],地址不同
复杂点在于,指针数组里存的是指针,而这些指针又指向别的地方——两层间接性,稍不注意就会改错层级。










