Skip to content

Go 指针

指针是 Go 语言中的一个基本概念,允许您直接处理内存地址。虽然 Go 语言自动管理内存(垃圾回收),但在某些任务中,指针是必不可少的,例如:

  • 直接修改函数参数(按引用传递语义)。
  • 高效地将大型数据结构传递给函数,避免复制。
  • 使用某些标准库 API(例如,带有指针接收者的方法)。
  • 表示可选值或可空值(nil 指针)。

Go 中的每个变量都位于特定的内存位置,由其地址标识。您可以使用取地址运算符(&)获取变量的内存地址。

package main
import "fmt"
func main() {
a := 10
fmt.Printf("Value of 'a': %d\n", a)
// 使用 %p 格式说明符以十六进制打印内存地址
fmt.Printf("Memory address of 'a': %p\n", &a)
}

执行时,可能会产生以下输出:

Value of 'a': 10
Memory address of 'a': 0xc000018030

(每次运行程序时,具体地址都会有所不同)。

指针是一种特殊的变量类型,它存储另一个变量的内存地址。它 ‘指向’ 实际数据存储的位置。

要声明一个指针变量,您需要在类型名称前使用星号(*)。指针的零值为 nil。

// 一般语法:var pointerName *DataType
var ip *int // ip 是一个指向整数的指针
var fp *float64 // fp 是一个指向 float64 的指针
var sp *string // sp 是一个指向 string 的指针

类型 *int 表示“指向整数的指针”。指针本身(ip)持有一个内存地址,而它指向的位置预计包含一个 int 类型的值。

使用指针涉及三个主要步骤:

  1. 声明一个指针变量(例如,var ip *int)。
  2. 使用 & 运算符将变量的内存地址赋给指针(例如,ip = &a)。
  3. 使用解引用运算符(*)访问指针地址处存储的值(例如,value := *ip)。

示例:

package main
import "fmt"
func main() {
a := 20 // 实际变量声明
var ip *int // 指针变量声明(初始值为 nil)
ip = &a // 将 'a' 的地址存储到指针变量 'ip' 中
fmt.Printf("Address of 'a' variable: %p\n", &a)
// 存储在指针变量 'ip' 中的地址
fmt.Printf("Address stored in 'ip' variable: %p\n", ip)
// 使用指针访问值(解引用)
fmt.Printf("Value of *ip variable: %d\n", *ip)
// 您也可以通过指针修改原始变量
*ip = 30
fmt.Printf("New value of 'a' after modification via pointer: %d\n", a)
}

输出(地址会变化):

Address of 'a' variable: 0xc000018038
Address stored in 'ip' variable: 0xc000018038
Value of *ip variable: 20
New value of 'a' after modification via pointer: 30

Go 语言中任何指针类型的零值都是 nil。nil 指针不指向任何有效的内存位置。

试图解引用 nil 指针(即,当 ptr 为 nil 时访问 *ptr)将导致运行时 panic。

package main
import "fmt"
func main() {
var ptr *int // 已声明但未初始化,所以 ptr 为 nil
fmt.Printf("The value of ptr is: %p\n", ptr) // 通常打印 <nil> 或 0x0
if ptr == nil {
fmt.Println("ptr is a nil pointer")
}
// 以下行会导致 panic:
// fmt.Println(*ptr)
}

输出:

The value of ptr is: <nil>
ptr is a nil pointer

在解引用指针之前,务必检查它是否为 nil,特别是如果它可能尚未被分配有效地址。

if ptr != nil {
fmt.Println("Value:", *ptr) // 安全地解引用
}

虽然 Go 尽可能鼓励使用值语义,但在特定场景下,指针至关重要:

场景描述与好处
修改函数参数传递指针允许函数修改调用者传递的原始变量。
避免大型复制传递大型 struct 的指针比复制整个 struct 更节省开销。
带有指针接收者的方法需要修改接收者 struct 的方法必须使用指针接收者。
可选值 / 零值指针可以为 nil 来表示不存在,这与类型的实际零值(例如 int 的 0)不同。

Go 通常不鼓励指针算术(直接对地址进行加减),这在 C/C++ 中很常见。底层内存的访问通常通过切片或 unsafe 包中的特定函数处理,用于低级任务。

指针的指针(**int)是可能的,但在地道的 Go 代码中不如 C 语言常见。