ptr
在 Go 中,指针是一种非常重要的概念,它允许直接访问和操作变量的内存地址。以下是 Go 语言中关于指针的基本概念和使用方法:
1. 什么是指针?
指针是一种变量,存储的是另一个变量的内存地址。通过指针,你可以访问或修改指向的变量。
简单示例:
go
package main
import "fmt"
func main() {
x := 42 // 定义变量 x
p := &x // 获取 x 的地址,并存储在指针 p 中
fmt.Println(p) // 输出指针 p 的值,即变量 x 的内存地址
fmt.Println(*p) // 通过指针 p 访问 x 的值(解引用操作)
*p = 21 // 修改指针 p 指向的变量的值
fmt.Println(x) // 输出变量 x 的新值
}输出结果:
0xc000014088 // x 的内存地址
42 // x 的值
21 // 修改后的 x 的值2. 指针的基本操作
获取变量地址:
使用 & 操作符来获取变量的内存地址。
go
p := &x // 获取变量 x 的地址解引用指针:
使用 * 操作符来访问指针指向的值。
go
fmt.Println(*p) // 输出 p 指向变量的值
*p = 100 // 修改 p 指向变量的值3. 指针与函数
指针常用于函数参数传递,使得函数可以直接修改传入变量的值。
示例:
go
package main
import "fmt"
func updateValue(p *int) {
*p = 99 // 修改指针指向的变量
}
func main() {
x := 10
fmt.Println("Before:", x)
updateValue(&x) // 传递变量 x 的地址
fmt.Println("After:", x)
}输出结果:
Before: 10
After: 994. 指针与结构体
指针可以指向结构体,并通过指针操作结构体的字段。
示例:
go
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
p := &Person{Name: "Alice", Age: 30} // 指针指向结构体
fmt.Println(p.Name) // 可以直接访问字段
fmt.Println(p.Age)
p.Age = 31 // 修改结构体字段
fmt.Println(p.Age)
}输出结果:
Alice
30
31Go 的指针允许直接通过指针访问结构体字段,而不需要显式解引用(即 p.Name 等价于 (*p).Name)。
5. 指针与切片、数组、Map
- 切片(Slice): Go 的切片本身是引用类型,传递切片时,无需显式使用指针。
- 数组(Array): 数组是值类型,传递数组时可以使用指针以避免拷贝。
- Map: Map 是引用类型,不需要使用指针传递。
数组指针示例:
go
package main
import "fmt"
func updateArray(arr *[3]int) {
arr[0] = 99
}
func main() {
a := [3]int{1, 2, 3}
updateArray(&a) // 传递数组的指针
fmt.Println(a)
}输出结果:
[99 2 3]6. 为什么 Go 没有指针运算?
与 C/C++ 不同,Go 中的指针不能执行加法、减法等运算。这是 Go 设计的一个特性,用于避免因为指针运算导致的内存安全问题。
7. 指针的零值
指针的零值是 nil。一个未初始化的指针默认值为 nil,不能直接解引用。
示例:
go
package main
import "fmt"
func main() {
var p *int // 未初始化的指针
if p == nil {
fmt.Println("Pointer is nil")
}
// fmt.Println(*p) // 解引用会引发运行时错误
}8. 指针与垃圾回收
Go 中的指针由垃圾回收器管理,开发者无需手动释放内存。这使得指针的使用更加安全和简洁。
9. new 和 make 的区别
new: 用于分配内存,返回指向该内存的指针。make: 用于初始化特定的引用类型(切片、Map、Channel)。
示例:
go
p := new(int) // 分配内存,返回指针
*p = 42
fmt.Println(*p) // 输出 42
m := make(map[string]int) // 初始化 Map
m["key"] = 100
fmt.Println(m["key"]) // 输出 100总结
- 指针的核心作用: 高效传递和修改数据,减少内存拷贝。
- 使用场景: 函数参数传递、结构体嵌套、动态内存管理等。
- 指针安全: Go 避免了指针运算和手动释放内存,提升了代码安全性和稳定性。