go语言-指针

什么是指针?

指针是存储一个变量的内存地址的,声明一个指针变量的格式如下:

Go 复制代码
// ptr是指针变量名,*表示是指针类型,*int表示存储的是一个int类型变量的内存地址
var ptr *int

1、关键操作符

|----|------|-------------------------------------------------|----------------------------------------------------------------|
| 符号 | 名称 | 作用 | 示例 |
| & | 取址符 | 获取变量在内存中的地址,返回指针类型 | ptr := &num (获取 num 的地址) |
| * | 解引用符 | 1. ‌声明时‌:表示指针类型(如 *int) 2. ‌使用时‌:获取或修改指针指向的具体值 | val := *ptr (读取ptr指向的int变量值) *ptr = 100 (修改ptr指向的int变量值为100) |

2、常见操作

Go 复制代码
package main

import "fmt"

func main() {
    // 1.定义普通变量
    num := 100
    fmt.Printf("普通变量值: %d, 地址: %p\n", num, &num)

    // 2. 声明并初始化指针
    var ptr *int = &num // ptr 存储的是 num 的内存地址
    
    // 3. 解引用:读取和修改
    fmt.Printf("指针指向的值: %d\n", *ptr) // 输出 100
    
    *ptr = 200 // 通过指针修改原始变量的值
    fmt.Printf("修改后原始变量值: %d\n", num) // 输出 200,证明原值被改变
}

3、空指针

当指针变量未初始化时,默认为空指针,nil。

go语言底层将nil视为地址为0的特殊变量。

所以,空指针 ptr == nil 为true,但是在打印ptr指向的内存地址时,打印出的是 0x0【就是0】

危险操作:对 nil 指针进行解引用(如 *ptr)会导致程序运行时崩溃(panic: invalid memory address or nil pointer dereference)。

Go 复制代码
package main

import "fmt"

func main() {
    
	var ptr *int
	if ptr == nil {
		fmt.Printf("%p", ptr)
	} 
}

4、指针的使用场景

4.1、在函数间共享数据并修改原值

Go 语言默认参数传递是‌值传递‌(拷贝副本)。如果希望在函数内部修改外部变量的值,必须传递指针。

Go 复制代码
func increment(ptr *int) {
    *ptr++ // 修改指针指向的原始值
}

func main() {
    count := 10
    increment(&count) // 传入地址
    fmt.Println(count) // 输出 11
}
4.2、避免大型数据结构的拷贝开销

对于大型结构体(Struct)或数组,值传递会复制整个数据结构,消耗内存和 CPU。传递指针仅复制一个地址(64位系统占8字节),效率极高。

Go 复制代码
type BigData struct {
    Items int
}

// 使用指针接收,避免拷贝 10000 个 int
func process(data *BigData) {
    // 处理逻辑
}

5、注意事项

1. 不可取地址的情况

你不能对以下对象使用 & 取地址,否则编译报错 cannot take the address of ...

  • 字面量/常量 ‌:&42&"hello" 是错误的。

  • Map 的值 ‌:&m["key"] 是错误的。因为 Map 的值可能随时移动或不存在,Go 禁止直接取 Map 值的地址。如果需要,先将值赋给临时变量再取址。

  • 函数返回值‌:通常情况下,不能直接对函数返回值取地址(除非返回的是指针或可寻址变量)。

2. 切片、Map 和 Channel 的特殊性
  • 切片(Slice)、Map 和 Channel 在 Go 中本身就是‌引用类型‌(底层实现包含指针)。

  • 无需传指针‌:在函数间传递切片或 Map 时,通常直接传递值即可。函数内部对元素内容的修改会反映到原变量上。

  • 例外 ‌:如果你需要在函数内改变切片的‌长度 ‌或‌容量 ‌(例如 append 导致底层数组重新分配),或者替换整个 Map 对象,则需要传递指向切片或 Map 的指针(如 *[]int),但这在日常开发中较少见。

3. 内存逃逸与性能
  • 栈与堆‌:Go 编译器会自动进行"逃逸分析"。如果局部变量的地址被返回或传递给其他 goroutine,该变量会分配到堆上,增加垃圾回收(GC)压力。

  • 适度使用‌:不要滥用指针。对于小的基本类型(int, bool, small struct),值传递往往比指针传递更快,因为指针涉及间接寻址和可能的堆分配。

4. 原子指针与 unsafe
  • sync/atomic.Pointer 提供了无锁的原子指针操作,适用于高并发场景下的状态切换。

  • unsafe.Pointer 允许绕过类型系统进行指针转换,极其危险,不参与 GC 生命周期检查,容易导致悬垂指针或内存破坏。除非编写底层库或驱动,否则严禁在业务代码中使用。

相关推荐
大都督会赢的10 小时前
数据结构(1)--顺序表
c语言·数据结构·学习·指针
讲不出 再见11 小时前
go语言-包
golang·go·package··包冲突
王中阳Go1 天前
用Go写AI Agent:我从实战图书里总结了这些核心逻辑
后端·go·ai编程
扉页的墨2 天前
Go 错误处理之道:别再到处 return fmt.Errorf 了,你的代码正在失控
go
止语Lab3 天前
你写的Go代码,编译器能"看懂"多少
go
刀法如飞4 天前
Go数组去重的20种实现方式,AI时代解决问题的不同思路
后端·算法·go
AI编程探险者4 天前
Go 编译的二进制突然跑不起来了?凶手是 macOS 的 syspolicyd
go
jieyucx4 天前
Go语言切片:动态灵活的数据序列
算法·golang·指针·顺序表·数组·结构体·切片
用户398346161204 天前
10 个示例快速入门 Go-Spring|v1.3.0 正式发布
go