一、数组指针 vs 指针数组

1. 数组指针(Pointer to Array)

  • 定义:指向整个数组内存地址的指针
  • 核心特性
    • 操作整个数组的引用
    • 通过指针修改数组元素会影响原数组
  • 典型应用:传递大数组避免拷贝
// 定义
var arr = [3]int{1, 2, 3}
var arrPtr *[3]int = &arr

// 通过指针访问元素
(*arrPtr)[0] = 100
fmt.Println(arr) // 输出 [100 2 3]

// 简化解引用(Go语法糖)
arrPtr[1] = 200 // 等价于 (*arrPtr)[1]

2. 指针数组(Array of Pointers)

  • 定义:数组元素全是指针类型
  • 核心特性
    • 每个元素指向独立内存地址
    • 灵活管理多个独立对象
  • 典型应用:管理动态分配的对象集合
// 定义
var ptrArr [3]*int

// 初始化
a, b, c := 10, 20, 30
ptrArr = [3]*int{&a, &b, &c}

// 修改值
*ptrArr[0] = 100
fmt.Println(a) // 输出100

对比总结

特性 数组指针 指针数组
类型 *[N]T [N]*T
内存占用 单个指针(8字节) N个指针(N×8字节)
适用场景 批量操作数组 管理多个独立对象

二、函数指针 vs 指针函数

1. 函数指针(Function Pointer)

  • 定义:指向函数的指针变量
  • 核心特性
    • 可作为参数传递或返回值
    • 实现回调机制和策略模式
  • 典型应用:动态选择算法逻辑
// 声明函数类型
type MathFunc func(int, int) int

// 函数赋值给变量
var add MathFunc = func(a, b int) int { return a + b }
var multiply MathFunc = func(a, b int) int { return a * b }

// 使用函数指针
func Calculate(x, y int, op MathFunc) int {
    return op(x, y)
}

fmt.Println(Calculate(3, 5, add))      // 输出8
fmt.Println(Calculate(3, 5, multiply)) // 输出15

2. 指针函数(Function Returning Pointer)

  • 定义:返回指针类型的函数
  • 核心特性
    • 返回堆内存对象的引用
    • 编译器自动处理逃逸分析
  • 典型应用:工厂模式创建对象
// 创建结构体实例
type User struct{ Name string }

func NewUser(name string) *User {
    return &User{Name: name} // 编译器确保分配到堆
}

// 使用
u := NewUser("Alice")
fmt.Println(u.Name) // 输出Alice

对比总结

特性 函数指针 指针函数
本质 func(...) ... 类型的变量 返回指针的函数
内存管理 不涉及内存分配 可能触发堆分配
典型用途 回调、策略模式 对象工厂、大对象返回

三、综合应用场景

1. 高效矩阵运算(数组指针)

func ProcessMatrix(mat *[1024][1024]float64) {
    // 直接操作原矩阵数据
    for i := range mat {
        for j := range mat[i] {
            mat[i][j] *= 2
        }
    }
}

var bigMatrix [1024][1024]float64
ProcessMatrix(&bigMatrix) // 避免4MB的数组拷贝

2. 动态插件系统(函数指针)

var pluginFuncs = make(map[string]func(string))

func RegisterPlugin(name string, fn func(string)) {
    pluginFuncs[name] = fn
}

func RunPlugin(name, arg string) {
    if fn, exists := pluginFuncs[name]; exists {
        fn(arg)
    }
}

// 注册插件
RegisterPlugin("logger", func(msg string) {
    fmt.Println("[LOG]", msg)
})

// 调用插件
RunPlugin("logger", "system started")

四、关键注意事项

1. 指针安全性

  • 空指针检查:解引用前验证nil
  • 并发访问:共享指针数据需同步
var unsafePtr *int

// 错误示例
// *unsafePtr = 10 // panic

// 正确做法
if unsafePtr != nil {
    *unsafePtr = 10
}

2. 内存管理优化

  • 逃逸分析:通过go build -gcflags="-m"查看
  • 重用对象:配合sync.Pool减少GC压力
var userPool = sync.Pool{
    New: func() interface{} { return new(User) },
}

func GetUser() *User {
    return userPool.Get().(*User)
}

func ReleaseUser(u *User) {
    userPool.Put(u)
}

五、性能对比测试

1. 数组传递性能

// 值传递(产生拷贝)
func PassArray(arr [1000]int) int { ... }

// 指针传递(无拷贝)
func PassArrayPtr(arr *[1000]int) int { ... }

// Benchmark结果(示例):
// PassArray-8     5000000    285 ns/op
// PassArrayPtr-8  20000000   82.3 ns/op

2. 函数调用开销

// 直接调用
func DirectCall(a, b int) int { return a + b }

// 通过函数指针调用
type FuncType func(int, int) int
var fp FuncType = DirectCall

// Benchmark结果(示例):
// DirectCall-8     1000000000  0.295 ns/op
// FuncPointer-8    1000000000  0.296 ns/op

总结:核心区别与应用指南

术语 定义要点 典型应用场景 性能影响
数组指针 指向整个数组的指针 大数组传递、批量修改 减少内存拷贝
指针数组 元素为指针的数组 管理动态对象集合 增加指针追踪开销
函数指针 存储函数地址的变量 回调机制、插件系统 几乎无额外开销
指针函数 返回指针的函数 对象工厂、大对象返回 可能增加GC压力
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。