指针是存储另一个变量内存地址的变量。Go语言保留了指针,但不支持指针运算(如C语言的++/--),从而减少了滥用指针带来的风险。指针在Go中主要用于:让函数能够修改外部变量(类似引用传递)、避免大数据的值拷贝、以及实现方法接收者(后续章节详述)。
nil;使用&取地址,*解引用。
使用*类型表示指向该类型的指针。使用&操作符获取变量的内存地址。
package main
import "fmt"
func main() {
var num int = 42
var ptr *int // 声明一个指向 int 的指针,初始值为 nil
ptr = &num // ptr 存储 num 的地址
fmt.Println("num 的值:", num) // 42
fmt.Println("num 的地址:", &num) // 如 0xc0000180a8
fmt.Println("ptr 的值:", ptr) // 与 &num 相同
fmt.Println("ptr 指向的值:", *ptr) // 解引用,输出 42
}
也可以在声明时直接初始化:ptr := &num。
通过在指针变量前加*,可以访问或修改指针指向的值。
x := 10
p := &x
fmt.Println(*p) // 读取 x 的值: 10
*p = 20 // 通过指针修改 x
fmt.Println(x) // 20
未初始化的指针值为nil,不能对nil指针解引用(会导致panic)。
var p *int
fmt.Println(p == nil) // true
// fmt.Println(*p) // panic: runtime error: invalid memory address
// 安全地解引用
if p != nil {
fmt.Println(*p)
} else {
fmt.Println("指针为 nil,无法解引用")
}
Go的所有参数都是值传递,但传递指针时可以修改原始变量。
func zeroVal(val int) {
val = 0
}
func zeroPtr(ptr *int) {
*ptr = 0
}
func main() {
x := 100
zeroVal(x) // 不会修改 x
fmt.Println(x) // 100
zeroPtr(&x) // 传递指针,会修改 x
fmt.Println(x) // 0
}
访问结构体指针的字段时,Go允许使用.直接访问,无需显式解引用(语法糖)。
type Person struct {
Name string
Age int
}
func main() {
p := Person{"Alice", 30}
ptr := &p
// 两种方式等价
(*ptr).Age = 31 // 显式解引用
ptr.Name = "Bob" // 语法糖,自动解引用
fmt.Println(p) // {Bob 31}
}
new(T) 分配内存,返回类型为*T的指针,指向该类型的零值内存。
p := new(int) // p 是 *int,指向的 int 值为 0
fmt.Println(*p) // 0
*p = 5
fmt.Println(*p) // 5
// 对于结构体
type Point struct{ X, Y int }
pt := new(Point)
pt.X = 10
pt.Y = 20
fmt.Println(pt) // &{10 20}
new 与 &T{} 的区别:new(T) 返回指针,且只能初始化零值;而 &T{} 可以指定非零初始值。推荐使用 &T{} 字面量方式。Go支持多级指针,但通常不够优雅,在实际开发中很少使用。
x := 10
p := &x // *int
pp := &p // **int
fmt.Println(**pp) // 10
切片和map本身已经包含了对底层数据的引用,通常不需要额外的指针。但有时需要修改切片本身(如append并需要反映到外部),可以传递切片指针。
func addElement(s *[]int, val int) {
*s = append(*s, val)
}
nums := []int{1,2}
addElement(&nums, 3)
fmt.Println(nums) // [1,2,3]
但更推荐的做法是返回新切片。
nil指针解引用,操作前应检查指针是否为nil。package main
import "fmt"
func swap(a, b *int) {
*a, *b = *b, *a
}
func main() {
x, y := 5, 10
fmt.Printf("交换前: x=%d, y=%d\n", x, y)
swap(&x, &y)
fmt.Printf("交换后: x=%d, y=%d\n", x, y)
}