指针
指针基本概念
指针是一个变量,存储了另一个变量的内存地址。
声明和使用指针
go
package main
import "fmt"
func main() {
// 声明变量
var num int = 100
// 声明指针
var ptr *int
// 获取变量地址
ptr = &num
// 输出
fmt.Println("num的值:", num) // 100
fmt.Println("num的地址:", &num) // 0x...
fmt.Println("ptr的值:", ptr) // 0x... (与&num相同)
fmt.Println("ptr指向的值:", *ptr) // 100
// 通过指针修改值
*ptr = 200
fmt.Println("修改后num的值:", num) // 200
}指针符号
| 符号 | 含义 | 示例 |
|---|---|---|
& | 取地址 | &num 获取num的地址 |
* | 取值(解引用) | *ptr 获取ptr指向的值 |
指针的零值
指针的零值是 nil:
go
var ptr *int
fmt.Println(ptr) // <nil>
// 检查指针是否为nil
if ptr == nil {
fmt.Println("指针为空")
}使用new创建指针
go
// 使用new分配内存并返回指针
ptr := new(int)
fmt.Println(*ptr) // 0 (零值)
*ptr = 100
fmt.Println(*ptr) // 100指针作为函数参数
值传递 vs 指针传递
go
package main
import "fmt"
// 值传递 - 不修改原值
func incrementByValue(x int) {
x += 1
}
// 指针传递 - 修改原值
func incrementByPointer(x *int) {
*x += 1
}
func main() {
num := 10
// 值传递
incrementByValue(num)
fmt.Println("值传递后:", num) // 10 (未改变)
// 指针传递
incrementByPointer(&num)
fmt.Println("指针传递后:", num) // 11 (已改变)
}交换两个变量
go
func swap(a, b *int) {
*a, *b = *b, *a
}
func main() {
x, y := 1, 2
fmt.Printf("交换前: x=%d, y=%d\n", x, y)
swap(&x, &y)
fmt.Printf("交换后: x=%d, y=%d\n", x, y)
}指针与切片
切片本身已经是引用类型,通常不需要使用指针:
go
func modifySlice(s []int) {
s[0] = 100 // 会修改原切片
}
func main() {
nums := []int{1, 2, 3}
modifySlice(nums)
fmt.Println(nums) // [100 2 3]
}但如果需要修改切片本身(如append),则需要传递指针:
go
func appendSlice(s *[]int, val int) {
*s = append(*s, val)
}
func main() {
nums := []int{1, 2, 3}
appendSlice(&nums, 4)
fmt.Println(nums) // [1 2 3 4]
}指针与Map
Map也是引用类型,不需要使用指针:
go
func modifyMap(m map[string]int) {
m["new"] = 100 // 会修改原map
}
func main() {
m := map[string]int{"a": 1}
modifyMap(m)
fmt.Println(m) // map[a:1 new:100]
}指针数组与数组指针
go
// 指针数组 - 元素是指针的数组
var ptrArr [3]*int
// 数组指针 - 指向数组的指针
var arrPtr *[3]int示例
go
func main() {
a, b, c := 1, 2, 3
// 指针数组
ptrArr := [3]*int{&a, &b, &c}
for i, ptr := range ptrArr {
fmt.Printf("ptrArr[%d] = %d\n", i, *ptr)
}
// 数组指针
arr := [3]int{1, 2, 3}
arrPtr := &arr
fmt.Println(*arrPtr) // [1 2 3]
}多级指针
go
func main() {
num := 100
ptr1 := &num // 一级指针
ptr2 := &ptr1 // 二级指针
fmt.Println(num) // 100
fmt.Println(*ptr1) // 100
fmt.Println(**ptr2) // 100
}何时使用指针
适合使用指针的场景
- 需要修改函数外的变量
go
func updateValue(val *int) {
*val = 100
}- 大结构体传递,避免拷贝
go
func processData(data *LargeStruct) {
// 避免拷贝大结构体
}- 表示可选值(nil表示无值)
go
type Config struct {
Timeout *int // nil表示使用默认值
}- 实现修改方法
go
func (r *Rectangle) Scale(factor float64) {
r.Width *= factor
r.Height *= factor
}不需要使用指针的场景
- 切片和Map(已经是引用类型)
- 小型基本类型
- 只读操作
指针注意事项
避免悬空指针
go
func danglingPointer() *int {
x := 10
return &x // Go会自动处理,x会逃逸到堆上
}检查nil指针
go
func safeAccess(ptr *int) int {
if ptr == nil {
return 0
}
return *ptr
}不要返回局部变量的指针
Go的逃逸分析会自动处理这种情况,但最好避免:
go
// 可以工作,但不推荐
func newInt() *int {
x := 10
return &x
}
// 推荐使用new
func newIntBetter() *int {
return new(int)
}