1.6 指针
指针的基本概念
指针存储变量的内存地址。Go 的指针比 C 更安全:没有指针运算,有垃圾回收。
package main
import "fmt"
func main() {
x := 42
p := &x // &x 取地址,p 是 *int 类型
fmt.Println(p) // 0x9867cc14020(内存地址)
fmt.Println(*p) // 42(解引用,获取值)
*p = 100 // 通过指针修改原变量
fmt.Println(x) // 100
// 指针的零值是 nil
var pp *int
fmt.Println(pp) // <nil>
fmt.Println(pp == nil) // true
}
值传递 vs 指针传递
Go 中函数参数全部是值传递,传指针实际上是传了地址的拷贝。
// 值传递:函数内修改不影响原变量
func doubleValue(n int) {
n *= 2
}
// 指针传递:通过指针修改原变量
func doublePointer(n *int) {
*n *= 2
}
func main() {
x := 10
doubleValue(x)
fmt.Println(x) // 10(没变)
doublePointer(&x)
fmt.Println(x) // 20(变了)
}
结构体与指针
type User struct {
Name string
Age int
}
// 值传递:拷贝整个结构体(大结构体效率低)
func birthdayValue(u User) {
u.Age++ // 不影响原结构体
}
// 指针传递:只拷贝地址(推荐)
func birthdayPointer(u *User) {
u.Age++ // 修改原结构体
}
func main() {
user := User{Name: "Alice", Age: 25}
birthdayValue(user)
fmt.Println(user.Age) // 25
birthdayPointer(&user)
fmt.Println(user.Age) // 26
}
new 与 make
func main() {
// new:分配内存,返回指针,值为零值
p := new(int) // *int,值为 0
fmt.Println(*p) // 0
u := new(User) // *User,各字段为零值
u.Name = "Bob"
// make:只用于 slice、map、channel,返回值(不是指针)
s := make([]int, 5)
m := make(map[string]int)
ch := make(chan int)
_ = s; _ = m; _ = ch
}
| 函数 | 适用类型 | 返回值 |
|---|---|---|
new(T) | 任意类型 | *T(指针) |
make(T) | slice、map、channel | T(值) |
何时使用指针
// ✅ 需要修改调用者的变量
func reset(p *int) { *p = 0 }
// ✅ 避免大结构体拷贝(通常 > 64 字节考虑用指针)
func processLargeStruct(data *LargeStruct) { ... }
// ✅ 表示"可选"值(nil 代表不存在)
func find(id int) *User { ... } // 返回 nil 表示没找到
// ❌ 小的值类型(int、bool)不需要指针
// ❌ 不需要修改原值时不需要指针
指针安全
func main() {
// Go 没有指针运算
// p++ // ❌ 编译错误
// Go 有垃圾回收(GC),不需要手动释放内存
p := new(int)
*p = 42
// 不需要 free(p),GC 会自动回收
// 返回局部变量的指针是安全的(Go 会自动逃逸到堆上)
pp := createPointer()
fmt.Println(*pp) // 100
}
func createPointer() *int {
x := 100
return &x // ✅ 安全!Go 编译器会将 x 分配到堆上
}
小结
| 要点 | 说明 |
|---|---|
& | 取地址 |
* | 解引用 / 指针类型声明 |
| 值传递 | Go 所有传参都是值传递 |
| 指针传递 | 传地址的拷贝,可以修改原值 |
| 安全 | 无指针运算、有 GC、可返回局部指针 |