Skip to content

指针

指针基本概念

指针是一个变量,存储了另一个变量的内存地址。

声明和使用指针

go
package main

import "fmt"

func main() {
    // 声明变量
    var num int = 100
    
    // 声明指针
    var ptr *int
    
    // 获取变量地址
    ptr = &num
    
    // 输出
    fmt.Println("num的值:", num)           // 100
    fmt.Println("num的地址:", &num)       // 0x...
    fmt.Println("ptr的值:", ptr)          // 0x... (与&num相同)
    fmt.Println("ptr指向的值:", *ptr)     // 100
    
    // 通过指针修改值
    *ptr = 200
    fmt.Println("修改后num的值:", num)    // 200
}

指针符号

符号含义示例
&取地址&num 获取num的地址
*取值(解引用)*ptr 获取ptr指向的值

指针的零值

指针的零值是 nil

go
var ptr *int
fmt.Println(ptr)  // <nil>

// 检查指针是否为nil
if ptr == nil {
    fmt.Println("指针为空")
}

使用new创建指针

go
// 使用new分配内存并返回指针
ptr := new(int)
fmt.Println(*ptr)  // 0 (零值)

*ptr = 100
fmt.Println(*ptr)  // 100

指针作为函数参数

值传递 vs 指针传递

go
package main

import "fmt"

// 值传递 - 不修改原值
func incrementByValue(x int) {
    x += 1
}

// 指针传递 - 修改原值
func incrementByPointer(x *int) {
    *x += 1
}

func main() {
    num := 10
    
    // 值传递
    incrementByValue(num)
    fmt.Println("值传递后:", num)  // 10 (未改变)
    
    // 指针传递
    incrementByPointer(&num)
    fmt.Println("指针传递后:", num)  // 11 (已改变)
}

交换两个变量

go
func swap(a, b *int) {
    *a, *b = *b, *a
}

func main() {
    x, y := 1, 2
    fmt.Printf("交换前: x=%d, y=%d\n", x, y)
    
    swap(&x, &y)
    fmt.Printf("交换后: x=%d, y=%d\n", x, y)
}

指针与切片

切片本身已经是引用类型,通常不需要使用指针:

go
func modifySlice(s []int) {
    s[0] = 100  // 会修改原切片
}

func main() {
    nums := []int{1, 2, 3}
    modifySlice(nums)
    fmt.Println(nums)  // [100 2 3]
}

但如果需要修改切片本身(如append),则需要传递指针:

go
func appendSlice(s *[]int, val int) {
    *s = append(*s, val)
}

func main() {
    nums := []int{1, 2, 3}
    appendSlice(&nums, 4)
    fmt.Println(nums)  // [1 2 3 4]
}

指针与Map

Map也是引用类型,不需要使用指针:

go
func modifyMap(m map[string]int) {
    m["new"] = 100  // 会修改原map
}

func main() {
    m := map[string]int{"a": 1}
    modifyMap(m)
    fmt.Println(m)  // map[a:1 new:100]
}

指针数组与数组指针

go
// 指针数组 - 元素是指针的数组
var ptrArr [3]*int

// 数组指针 - 指向数组的指针
var arrPtr *[3]int

示例

go
func main() {
    a, b, c := 1, 2, 3
    
    // 指针数组
    ptrArr := [3]*int{&a, &b, &c}
    for i, ptr := range ptrArr {
        fmt.Printf("ptrArr[%d] = %d\n", i, *ptr)
    }
    
    // 数组指针
    arr := [3]int{1, 2, 3}
    arrPtr := &arr
    fmt.Println(*arrPtr)  // [1 2 3]
}

多级指针

go
func main() {
    num := 100
    ptr1 := &num     // 一级指针
    ptr2 := &ptr1    // 二级指针
    
    fmt.Println(num)     // 100
    fmt.Println(*ptr1)   // 100
    fmt.Println(**ptr2)  // 100
}

何时使用指针

适合使用指针的场景

  1. 需要修改函数外的变量
go
func updateValue(val *int) {
    *val = 100
}
  1. 大结构体传递,避免拷贝
go
func processData(data *LargeStruct) {
    // 避免拷贝大结构体
}
  1. 表示可选值(nil表示无值)
go
type Config struct {
    Timeout *int  // nil表示使用默认值
}
  1. 实现修改方法
go
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

不需要使用指针的场景

  1. 切片和Map(已经是引用类型)
  2. 小型基本类型
  3. 只读操作

指针注意事项

避免悬空指针

go
func danglingPointer() *int {
    x := 10
    return &x  // Go会自动处理,x会逃逸到堆上
}

检查nil指针

go
func safeAccess(ptr *int) int {
    if ptr == nil {
        return 0
    }
    return *ptr
}

不要返回局部变量的指针

Go的逃逸分析会自动处理这种情况,但最好避免:

go
// 可以工作,但不推荐
func newInt() *int {
    x := 10
    return &x
}

// 推荐使用new
func newIntBetter() *int {
    return new(int)
}