int是指向单个int的指针,非数组指针;它仅存储一个地址,解引用只访问8字节,不隐含长度信息;操作多个int应使用切片[]int或数组指针[N]int。

Go 里 *int 不是指向数组,而是指向单个 int
很多人看到 *int 就下意识觉得“这该是数组指针”,其实不是。*int 是一个指向单个 int 值的指针,和数组无关。它只存一个地址,那个地址后面只放一个 int(通常是 8 字节),不会自动延伸成数组。想操作多个 int,得靠切片、数组变量本身取地址,或者手动算偏移——但后者在 Go 里不安全、不推荐。
常见错误现象:
• 把 &arr[0] 当成“数组指针”,传给接收 *int 的函数,结果只能读写第一个元素
• 对 *int 做 ptr + 1 类似 C 的指针算术,编译直接报错:invalid operation: ptr + 1 (mismatched types *int and int)
-
*int的底层就是 8 字节地址(64 位系统),解引用只读/写紧邻的那 8 字节 - 若你真需要“指向整个数组”,得用数组指针类型,比如
*[5]int,不是*int - 切片
[]int才是 Go 中安全操作连续int序列的惯用方式,它内部含指针、长度、容量三元组
如何正确获取数组首元素地址并安全访问后续元素
假设你有一个数组 arr := [3]int{10, 20, 30},想拿到首地址并像 C 那样“遍历”——Go 不允许裸指针算术,但可以用 unsafe(不推荐)或更稳妥的切片转换。
推荐做法:把数组转成切片,再通过切片索引访问,语义清晰且内存安全:
立即学习“go语言免费学习笔记(深入)”;
arr := [3]int{10, 20, 30}
slice := arr[:] // 类型 []int,底层数组仍是 arr
firstPtr := &slice[0] // 类型 *int,指向 arr[0]
// 后续访问用 slice[1], slice[2],不要对 firstPtr 做 +1
如果你非要用指针做偏移(例如对接 C 库),必须用 unsafe + uintptr,但极易越界或触发 GC 问题:
- 必须用
unsafe.Offsetof或unsafe.Sizeof计算偏移,不能硬写数字 - 要确保原数组不被 GC 回收(比如定义为包级变量,或用
runtime.KeepAlive) - 这种代码无法通过
go vet,也容易在 future Go 版本中失效
*[N]int 和 *int 的内存布局差异
*[5]int 是“指向一个长度为 5 的数组”的指针;*int 是“指向一个 int”的指针。二者类型不同、解引用行为完全不同。
示例对比:
arr := [5]int{1,2,3,4,5}
pArr := &arr // 类型 *[5]int
pInt := &arr[0] // 类型 *int
// ✅ 正确:pArr 解引用得到整个数组
x := *pArr // 类型 [5]int
// ✅ 正确:pInt 解引用只得到第一个 int
y := *pInt // 类型 int
// ❌ 错误:不能对 *int 解引用出数组
// z := *pInt[1] // 编译错误:invalid operation: pInt[1] (type *int is not indexable)
-
*[5]int解引用后是值拷贝整个数组(5×8=40 字节),开销大,一般只用于传递大数组避免复制时显式取地址 -
*int解引用是轻量的,但完全不具备“知道后面还有多少元素”的能力 - 两者在内存中都只存一个地址,区别仅在于编译器如何解释这个地址之后的内存范围
什么时候该用 *int,什么时候该用 []int 或 *[N]int
选型核心看你要表达的语义和控制粒度:
- 只需修改某个已有
int变量的值(如函数内改入参)→ 用*int - 要读写一段连续整数,长度可能变化 → 无条件用
[]int(最 Go idiomatic) - 确定长度固定、且需把整个数组作为不可分割单元传递(比如作为 map key,或调用要求
*[16]byte的 crypto 函数)→ 用*[N]int - 绝不要为了“模拟数组指针”而把
&slice[0]强转成*int再做算术——这是 bug 温床
容易被忽略的一点:*int 本身不携带任何长度信息,哪怕它来自数组,一旦脱离上下文,就彻底丢失“我在数组里”这个事实。Go 的类型系统故意不让你靠指针推断结构,这是保护,不是限制。










