Skip to content

指针

变量是一种使用方便的占位符,用于引用计算机内存地址。

而指针是一个变量,其值为另一个变量的地址,即,内存位置的直接地址。

定义

  • 使用*类型定义指针类型。
  • 使用&变量获取变量地址。
  • 使用*指针变量获取指针指向的值。
go
a := 10 /* 声明实际变量 */
b := &a /* 声明指针变量 */
fmt.Printf("a 变量的指针地址: %+v\n", &a)

/* 指针变量的值 */
fmt.Printf("b 指针变量的值: %+v\n", b)

/* 指针变量的存储地址 */
fmt.Printf("b 指针变量的指针地址: %x\n", &b)

/* 使用指针访问值 */
fmt.Printf("*b 变量的值: %+v\n", *b)

输出结果

a 变量的指针地址: 0x14000102020

b 指针变量的值: 0x14000102020

b 指针变量的指针地址: 0x140000a2028

*b 变量的值: 10

指针内存图

空指针

当一个指针被定义后没有分配到任何变量时,它的值为 nil

  • nil 指针也称为空指针。
  • nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
  • 一个指针变量通常缩写为 ptr。
go
var  ptr *int
fmt.Printf("ptr 的值为 : %x\n", ptr  )

输出结果

ptr 的值为 : 0

空指针判断

go
var  ptr *int
if ptr != nil {
    fmt.Println("ptr 不是空指针")
} else {
    fmt.Println("ptr 是空指针")
}

输出结果

ptr 是空指针

使用场景

指针在 Go 语言中有着广泛的应用场景,下面将从几个方面介绍指针的常见应用。

函数参数传递

通过指针,函数可以修改函数外部的变量。这在需要修改外部变量的值时非常有用,特别是在处理复杂数据结构或需要对全局状态进行修改的情况下。

go
package main

import "fmt"

func changeValue(ptr *int) {
	*ptr = 20 // 修改指针指向的值
}
func main() {
	x := 10
	changeValue(&x) // 传递x的地址给changeValue函数
	fmt.Println(x)  // 输出修改后的x的值,即20
}

传递大对象

在函数参数传递时,如果直接传递大对象的副本,会产生额外的内存开销。通过传递指针,可以避免复制整个对象,提高程序的性能。

go
package main

type BigObject struct {
	// 大对象的定义...
}

func processObject(obj *BigObject) {
	// 对大对象进行处理...
}

func main() {
	obj := BigObject{}
	processObject(&obj) // 传递大对象的指针
}

不使用场景

  • 不要对 map、slice、channel 这类引用类型使用指针
  • 像 int、bool 这样的小数据类型没必要使用指针
  • 如果需要并发安全,则尽可能地不要使用指针,使用指针一定要保证并发安全
  • 指针最好不要嵌套,也就是不要使用一个指向指针的指针,虽然 Go 语言允许这么做,但是这会使你的代码变得异常复杂。