Skip to content

ptr

在 Go 中,指针是一种非常重要的概念,它允许直接访问和操作变量的内存地址。以下是 Go 语言中关于指针的基本概念和使用方法:


1. 什么是指针?

指针是一种变量,存储的是另一个变量的内存地址。通过指针,你可以访问或修改指向的变量。

简单示例:

go
package main

import "fmt"

func main() {
    x := 42        // 定义变量 x
    p := &x        // 获取 x 的地址,并存储在指针 p 中

    fmt.Println(p)  // 输出指针 p 的值,即变量 x 的内存地址
    fmt.Println(*p) // 通过指针 p 访问 x 的值(解引用操作)

    *p = 21         // 修改指针 p 指向的变量的值
    fmt.Println(x)  // 输出变量 x 的新值
}

输出结果:

0xc000014088   // x 的内存地址
42             // x 的值
21             // 修改后的 x 的值

2. 指针的基本操作

获取变量地址:

使用 & 操作符来获取变量的内存地址。

go
p := &x  // 获取变量 x 的地址

解引用指针:

使用 * 操作符来访问指针指向的值。

go
fmt.Println(*p)  // 输出 p 指向变量的值
*p = 100         // 修改 p 指向变量的值

3. 指针与函数

指针常用于函数参数传递,使得函数可以直接修改传入变量的值。

示例:

go
package main

import "fmt"

func updateValue(p *int) {
    *p = 99 // 修改指针指向的变量
}

func main() {
    x := 10
    fmt.Println("Before:", x)

    updateValue(&x) // 传递变量 x 的地址
    fmt.Println("After:", x)
}

输出结果:

Before: 10
After: 99

4. 指针与结构体

指针可以指向结构体,并通过指针操作结构体的字段。

示例:

go
package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    p := &Person{Name: "Alice", Age: 30} // 指针指向结构体
    fmt.Println(p.Name)                 // 可以直接访问字段
    fmt.Println(p.Age)

    p.Age = 31 // 修改结构体字段
    fmt.Println(p.Age)
}

输出结果:

Alice
30
31

Go 的指针允许直接通过指针访问结构体字段,而不需要显式解引用(即 p.Name 等价于 (*p).Name)。


5. 指针与切片、数组、Map

  • 切片(Slice): Go 的切片本身是引用类型,传递切片时,无需显式使用指针。
  • 数组(Array): 数组是值类型,传递数组时可以使用指针以避免拷贝。
  • Map: Map 是引用类型,不需要使用指针传递。

数组指针示例:

go
package main

import "fmt"

func updateArray(arr *[3]int) {
    arr[0] = 99
}

func main() {
    a := [3]int{1, 2, 3}
    updateArray(&a) // 传递数组的指针
    fmt.Println(a)
}

输出结果:

[99 2 3]

6. 为什么 Go 没有指针运算?

与 C/C++ 不同,Go 中的指针不能执行加法、减法等运算。这是 Go 设计的一个特性,用于避免因为指针运算导致的内存安全问题。


7. 指针的零值

指针的零值是 nil。一个未初始化的指针默认值为 nil,不能直接解引用。

示例:

go
package main

import "fmt"

func main() {
    var p *int  // 未初始化的指针
    if p == nil {
        fmt.Println("Pointer is nil")
    }

    // fmt.Println(*p) // 解引用会引发运行时错误
}

8. 指针与垃圾回收

Go 中的指针由垃圾回收器管理,开发者无需手动释放内存。这使得指针的使用更加安全和简洁。


9. newmake 的区别

  • new: 用于分配内存,返回指向该内存的指针。
  • make: 用于初始化特定的引用类型(切片、Map、Channel)。

示例:

go
p := new(int)  // 分配内存,返回指针
*p = 42
fmt.Println(*p) // 输出 42

m := make(map[string]int) // 初始化 Map
m["key"] = 100
fmt.Println(m["key"]) // 输出 100

总结

  • 指针的核心作用: 高效传递和修改数据,减少内存拷贝。
  • 使用场景: 函数参数传递、结构体嵌套、动态内存管理等。
  • 指针安全: Go 避免了指针运算和手动释放内存,提升了代码安全性和稳定性。