Go 指针还能这么玩?结构体方法调用竟然不用解引用!

179 阅读2分钟

Go 语言中的指针详解

包含字段与方法的简化写法

在 Go 语言中,指针是高效操作数据、避免大对象拷贝的重要工具。本文将详细讲解 Go 中指针相关的几个关键概念:

  • &:取地址
  • *T:声明指针类型
  • *p:解引用指针
  • 结构体指针可以使用 ptr.Fieldptr.Method() 的简化写法
  • 是否支持多级指针如 **T

一、基本操作符解释

1. &:取地址

用于获取变量的内存地址。

a := 42
p := &a // p 是一个指向 int 的指针

此时 p 类型为 *int

2. *T:声明指针类型

var p *int // 声明一个指向 int 的指针

这里的 * 表示这是一个指针类型。

3. *p:解引用指针

fmt.Println(*p) // 输出 42
*p = 100        // 修改 a 的值为 100

表示访问指针所指向的实际值。

二、结构体指针的简化写法

Go 提供了两个非常实用的语法糖:

✅ 1. 字段访问:ptr.Field

type Person struct {
    Name string
}

func main() {
    p := Person{Name: "Alice"}
    ptr := &p

    fmt.Println(ptr.Name) // 合法,等价于 (*ptr).Name
}

Go 编译器会自动将 ptr.Name 转换为 (*ptr).Name

✅ 2. 方法调用:ptr.Method()

func (p *Person) SayHello() {
    fmt.Println("Hello, ", p.Name)
}

func main() {
    p := &Person{Name: "Alice"}
    p.SayHello() // 合法,等价于 (*p).SayHello()
}

Go 允许你直接使用指针调用方法,不需要显式解引用。

这是 Go 特有的设计特性,目的是让代码更简洁、易读。

三、函数参数传递与指针

如果希望函数能修改原始变量,通常需要传入指针。

func updateValue(v *int) {
    *v = 100
}

func main() {
    a := 10
    updateValue(&a)
    fmt.Println(a) // 输出 100
}

四、是否支持 **T 这样的多级指针?

Go 支持多级指针,例如:

func main() {
    a := 10
    p := &a
    pp := &p

    fmt.Println(**pp) // 输出 10
    **pp = 20
    fmt.Println(a)   // 输出 20
}

虽然语法上支持 **T,但在实际开发中不推荐使用,因为会增加理解成本和出错概率。

五、总结对比表

操作符/语法使用场景示例说明
&取变量地址p := &a获取变量 a 的内存地址
*T声明指针类型var p *intp 是一个指向 int 的指针
*p解引用指针fmt.Println(*p)获取 p 所指向的值
ptr.Field访问结构体字段ptr.Name等价于 (*ptr).Name
ptr.Method()调用结构体方法ptr.SayHello()等价于 (*ptr).SayHello()
**T多级指针var pp **int支持但不推荐使用

六、最佳实践建议

  • ✅ 使用指针避免大对象拷贝,提高性能。
  • ✅ 在函数中修改外部变量时,传入指针。
  • ✅ 对结构体指针使用 ptr.Fieldptr.Method() 简化写法,提升可读性。
  • ⚠️ 避免使用多级指针,除非必要。
  • ❌ 不要对非结构体指针使用 .Field.Method() 写法。