go指针
84 次阅读
一、指针基础概念
1. 什么是指针
- 变量存储的是值;指针变量存储的是另一个变量的内存地址。
- 通过地址可以直接操作原始变量,避免值拷贝开销、修改原值。
2. 两个核心操作符
&:取地址符,放在变量前,获取该变量内存地址*:解引用符,放在指针变量前,取出地址对应的值
3. 指针类型格式
var p *int
*int 代表:指向 int 类型变量的指针。
二、指针基础使用示例
package main
import "fmt"
func main() {
// 普通变量
var num int = 10
fmt.Printf("num的值:%d\n", num)
fmt.Printf("num的内存地址:%p\n", &num)
// 定义int类型指针,保存num地址
var p *int
p = &num
fmt.Printf("指针p存储的地址:%p\n", p)
fmt.Printf("指针p解引用的值:%d\n", *p)
// 通过指针修改原始变量
*p = 66
fmt.Printf("修改后num的值:%d\n", num)
}
输出说明:修改 *p 等价直接修改 num。
三、指针零值
指针未赋值时默认零值为 nil,代表不指向任何内存地址。
func main() {
var p *int
fmt.Println(p) // <nil>
// *p // 报错:解引用nil指针,程序崩溃
}
使用指针前必须判断是否为 nil,避免运行时panic。
四、new 函数创建指针
new(T) 会:
- 分配一块 T 类型内存
- 内存填充类型零值
- 返回该内存地址指针
func main() {
// 创建int指针,初始值0
p := new(int)
fmt.Println(*p) // 0
*p = 99
fmt.Println(*p) // 99
// 结构体指针
type User struct{ name string }
u := new(User)
u.name = "张三"
fmt.Println(u.name)
}
五、值类型与指针(核心场景)
Go 值类型:int、float、bool、数组、struct
值类型传参/赋值会完整拷贝,副本修改不影响原变量;传指针可操作原值。
示例:不传指针,无法修改外部变量
func changeAge(age int) {
age = 20
}
func main() {
a := 10
changeAge(a)
fmt.Println(a) // 10,原值不变
}
示例:传入指针,修改原始数据
func changeAge(age *int) {
*age = 20
}
func main() {
a := 10
changeAge(&a)
fmt.Println(a) // 20,原值被修改
}
六、数组指针
数组是值类型,大数组拷贝成本高,可用数组指针传递:
func modifyArr(arr *[3]int) {
(*arr)[0] = 99
}
func main() {
a := [3]int{1,2,3}
modifyArr(&a)
fmt.Println(a) // [99 2 3]
}
七、结构体指针
结构体指针访问字段时,Go 支持简写,无需手动解引用 (*stu).name,直接 stu.name。
package main
import "fmt"
type Student struct {
name string
age int
}
func main() {
s := Student{"小明", 16}
p := &s
// 两种写法等价
fmt.Println((*p).name)
fmt.Println(p.name) // 推荐简写
// 通过指针修改结构体字段
p.age = 17
fmt.Println(s.age)
}
结构体指针作为函数参数
func setName(s *Student, newName string) {
s.name = newName
}
func main() {
s := Student{}
setName(&s, "小红")
fmt.Println(s.name)
}
八、多级指针(*T)
指针也可以保存指针地址,格式 **int,日常开发极少使用。
func main() {
num := 10
p1 := &num // *int
p2 := &p1 // **int
fmt.Println(*p2) // 等价p1,num的地址
fmt.Println(**p2) // 等价num的值
}
九、指针与切片/map/channel(引用类型)
切片、map、channel 本身底层就是指针封装,不需要再传指针:
func modifySlice(s []int) {
s[0] = 99
}
func main() {
sl := []int{1,2,3}
modifySlice(sl)
fmt.Println(sl) // [99 2 3] 直接修改原切片数据
}
只有需要修改切片本身(长度、容量、底层数组地址)时才使用
*[]int。
十、指针方法(结构体接收者)
- 值接收者:方法接收结构体副本,无法修改原结构体
- 指针接收者:接收结构体指针,可修改原始结构体字段
type User struct {
name string
}
// 值接收者
func (u User) show() {
fmt.Println(u.name)
}
// 指针接收者
func (u *User) rename(newName string) {
u.name = newName
}
func main() {
u := User{"老王"}
u.rename("老李")
u.show() // 老李
}
十一、指针使用规范与注意事项
- nil 指针禁止解引用,使用前先判空
var p *int
if p != nil {
fmt.Println(*p)
}
- 大值类型(数组、大结构体)传参优先指针,减少内存拷贝
- 引用类型 slice/map/chan 一般无需额外套指针
- 不要返回函数内局部变量的指针(Go 逃逸分析可自动优化,但尽量避免)
- 多级指针可读性差,业务代码尽量不用
十二、指针核心总结
&变量:取地址;*指针:解引用取值- 指针零值 nil,nil 不可解引用
- new(T) 快速生成类型指针
- 值类型传参拷贝,指针可修改外部原始数据
- 结构体指针访问字段可省略解引用
- 切片、map、channel 属于引用类型,自带地址特性