商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > go语言指针常见操作方法

go语言指针常见操作方法

  发布于2026-05-21 阅读(0)

扫一扫,手机访问

聊到Go语言,指针总是一个绕不开的话题。它不像C语言里那样让人望而生畏,但也绝非可以掉以轻心的存在。今天,我们就来把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、常见操作

光说不练假把式,来看一段代码,把声明、取址、解引用的流程走一遍:

package main
import "fmt"

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

    // 2. 声明一个指针,并让它指向num的地址
    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,打印它的地址会显示0x0

这里有个大坑: 试图对一个nil指针进行解引用(比如执行*ptr),程序会立刻崩溃,并报错“panic: invalid memory address or nil pointer dereference”。这就好比拿着一个空地址去找房子,结果当然是找不到。

package main
import "fmt"

func main() {
	var ptr *int // 未初始化,ptr是nil
	if ptr == nil {
		fmt.Printf("%p", ptr) // 输出 0x0
	}
	// *ptr = 5 // 如果执行这行,程序会panic
}

4、指针的使用场景

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

Go语言的函数参数默认是值传递,也就是说,传进去的是个副本。如果你想在函数内部修改外部的那个“原件”,就必须传递指针。

func increment(ptr *int) {
    *ptr++ // 直接修改指针指向的那个原始值
}

func main() {
    count := 10
    increment(&count) // 把count的地址传进去
    fmt.Println(count) // 输出 11,原值被成功修改
}

4.2、避免大型数据结构的拷贝开销

想象一下,你有一个包含成千上万个元素的结构体或数组。如果按值传递,整个数据都会被复制一份,既费内存又耗CPU。传递指针就高效多了,它只复制一个内存地址(在64位系统上就8个字节)。

type BigData struct {
    Items [10000]int
}

// 使用指针接收,避免拷贝10000个int的巨大开销
func process(data *BigData) {
    // 在这里处理数据
    data.Items[0] = 1 // 修改会直接影响外部的原始结构体
}

5、注意事项

指针虽好,但用起来也得讲究章法,否则容易踩坑。

1. 不可取地址的情况

不是所有东西都能用&取地址。以下几种情况,编译器会直接报错“cannot take the address of ...”:

  • 字面量/常量:比如&42&"hello"。数字42和字符串“hello”本身没有固定的内存地址。
  • Map 的值:比如&m["key"]。因为Map底层结构可能会动态调整,其值的位置不固定,所以禁止直接取址。如果非要用,得先赋值给一个临时变量。
  • 函数返回值:一般情况下,不能直接对函数返回值取地址(除非函数返回的就是指针或可寻址的变量)。

2. 切片、Map 和 Channel 的特殊性

这是Go语言设计上的一个巧妙之处。

  • 切片(Slice)、Map和Channel,它们本身就被设计为引用类型。你可以把它们理解成自带了一个“内部指针”。
  • 无需传指针:在函数间传递这些类型时,直接传值就行。函数内部对元素内容的修改(比如修改切片某个索引的值,或给Map新增键值对),会直接反映到外部的原变量上。
  • 例外:如果你需要在函数里改变切片的长度或容量(比如append导致底层数组换了),或者想替换掉整个Map对象,那还是需要传递指针(如*[]int)。不过这种场景在日常开发中比较少见。

3. 内存逃逸与性能

  • 栈与堆:Go编译器很聪明,会做“逃逸分析”。如果一个局部变量的地址被返回,或者传给了其他goroutine,它就会被“赶到”堆内存上分配。这会增加垃圾回收(GC)的压力。
  • 适度使用:别滥用指针。对于像intbool或者很小的结构体这类“轻量级”数据,值传递往往更快。因为指针操作多了一次“寻址”的开销,还可能引发不必要的堆分配。

4. 原子指针与 unsafe

  • sync/atomic.Pointer:这是为高并发场景准备的“武器”,提供了无锁的原子指针操作,适合做状态切换。
  • unsafe.Pointer这是一把“双刃剑”。它能让你绕过Go的类型系统,直接进行指针转换,极其危险。它不参与GC的生命周期管理,用不好很容易导致“悬垂指针”(访问已释放内存)或内存破坏。除非你在写底层系统库、驱动或者追求极致性能的框架,否则在业务代码里请远离它。

总的来说,Go语言的指针在提供强大能力的同时,也通过语言设计规避了许多传统指针的陷阱。理解它的原理,明确它的适用场景和边界,你就能在需要的时候,游刃有余地使用这个工具。

本文转载于:https://www.jb51.net/jiaoben/363538yzd.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注