如何使用Golang实现指针数组_Golang数组元素指针访问与修改

Go中声明初始化指向数组元素的指针需用循环逐个取地址存入[]T切片,如ptrs[i] = &arr[i];直接修改ptrs[i]可更新原数组元素,但须确保指针生命周期内原数据未销毁。

怎么声明和初始化指向数组元素的指针

Go 里没有“指针数组”这种类型(即 []*int 是指针切片,不是 C 风格的指针数组),但你可以获取数组中每个元素的地址并存入一个指针切片。关键点是:数组本身是值类型,&arr[i] 得到的是第 i 个元素的地址,类型为 *int

常见错误是试图对整个数组取地址后做偏移(如 &arr + i),Go 不支持指针算术,也不允许对数组变量直接做算术运算。

  • 正确方式:用循环逐个取地址,存入 []*T 切片
  • 注意:如果原数组是局部变量,确保指针不逃逸到函数外后访问已销毁的栈内存(编译器通常能判断,但大数组或复杂闭包下需留意)
  • 数组长度固定,所以 len(arr) 可安全用于循环边界
arr := [3]int{10, 20, 30}
ptrs := make([]*int, len(arr))
for i := range arr {
    ptrs[i] = &arr[i]
}
// ptrs[0] 现在指向 arr[0],修改 *ptrs[0] 会改变 arr[0]

通过指针修改原数组元素是否生效

是的,只要指针指向的是原数组的元素地址,解引用并赋值就直接修改原位置。这是 Go 指针最基础也最可靠的用途——绕过值拷贝,实现就地更新。

容易踩的坑在于混淆“指针副本”和“被指向对象”。比如把 ptrs[i] 赋给另一个变量 p := ptrs[i]p 是新指针变量,但它仍指向同一地址,*p = 999 依然改的是原数组元素。

立即学习“go语言免费学习笔记(深入)”;

  • 修改操作:直接写 *ptrs[i] = newValue
  • 不要写 ptrs[i] = &newValue——这会让该指针指向一个新局部变量,原数组不受影响
  • 若原数组是函数参数传入,且你传的是数组值(如 func f(a [3]int)),那 &a[i] 指向的是副本,修改无效;必须传指针 *[3]int 或用切片

用切片代替数组时指针行为有啥不同

切片底层有底层数组,slice[i] 的地址仍是底层数组对应元素的地址,所以 &slice[i] 依然可用、可修改原数据。但要注意切片头可能被重新切(slice = slice[1:]),此时旧指针仍指向原底层数组位置,不一定再属于当前切片范围——Go 不检查越界解引用,运行时可能读到脏数据或引发 panic(取决于 GC 和内存状态)。

  • 安全前提:确保指针生命周期内,底层数组未被回收,且索引仍在有效范围内
  • 切片扩容(如 append 导致新底层数组)会使原有元素指针失效——旧指针仍指向老内存,但新切片已不包含它
  • 推荐:如需长期持有元素地址,优先用固定大小数组或显式管理底层数组(如 make([]int, n) 后不再 append)
data := make([]int, 3)
for i := range data {
    data[i] = i * 10
}
ptrs := make([]*int, len(data))
for i := range data {
    ptrs[i] = &data[i] // 这里 &data[i] 有效,指向底层数组
}
*ptrs[1] = 999 // data[1] 变成 999

为什么不能用 **int 来批量操作

有人想定义 pp := &ptrs(即 **int),再通过 **pp 改值,这是误解。Go 中 **int 是“指向 int 指针的指针”,它只能间接修改一个指针变量本身的值(比如让那个指针指向别处),不能用来遍历或批量解引用。

真正需要批量修改时,还是得遍历 []*int,逐个 *p = x。没有语法糖能跳过这一步。

  • pp := &ptrs[0]**int*pp*int**ppint —— 它只关联单个元素
  • 数组/切片的指针集合本质是独立指针的集合,不是连续内存块,无法用双重解引用统一处理
  • 性能上,遍历 []*T 并解引用是标准做法,现代 CPU 对 cache 友好,无需过度优化

实际用的时候,最常漏掉的是生命周期检查:传进来的数组或切片是不是临时的?指针存下来之后,原数据还在不在?这点比语法更关键。