📚 原创系列: “Go语言学习系列”
🔄 转载说明: 本文最初发布于"Gopher部落"微信公众号,经原作者授权转载。
🔗 关注原创: 欢迎扫描文末二维码,关注"Gopher部落"微信公众号获取第一手Go技术文章。
📑 Go语言学习系列导航
🚀 第一阶段:入门篇本文是【Go语言学习系列】的第9篇,当前位于第一阶段(入门篇)
- Go语言简介与环境搭建
- Go开发工具链介绍
- Go基础语法(一):变量与数据类型
- Go基础语法(二):流程控制
- Go基础语法(三):函数
- Go基础语法(四):数组与切片
- Go基础语法(五):映射
- Go基础语法(六):结构体
- Go基础语法(七):指针 👈 当前位置
- Go基础语法(八):接口
- 错误处理与异常
- 第一阶段项目实战:命令行工具
📖 文章导读
在本文中,您将学习:
- 指针的基本概念与内存模型
- Go指针的安全特性与C/C++指针的区别
- 指针的声明、初始化与操作方法
- 函数参数中值传递与指针传递的应用场景
- 结构体与指针的结合使用技巧
- 常见的指针陷阱与解决方案
- unsafe包的高级应用与注意事项
指针是Go语言中非常重要的特性,它既提供了直接操作内存的能力,又通过严格的安全限制避免了常见的内存问题。本文将帮助您全面掌握指针的使用,提升代码性能,同时避免常见陷阱。

Go基础语法(七):指针详解与最佳实践
指针是Go语言继承自C语言的重要特性,用于存储变量的内存地址。与C/C++不同,Go指针设计更加简洁和安全,既提供了直接操作内存的能力,又通过去除指针算术等危险操作防止了内存错误。本文将深入讲解Go语言指针的原理、应用和最佳实践。
一、指针基础
1.1 什么是指针
指针是一种特殊的变量,它不直接存储实际数据,而是存储另一个变量在内存中的地址。通过指针,我们可以:
- 直接访问和修改内存中的数据
- 避免大型数据的复制开销
- 实现引用传递(间接方式)
- 构建复杂的数据结构(如链表、树等)
在Go中,指针类型的声明使用*符号:
var p *int // 声明一个指向int类型的指针
1.2 指针的基本操作
Go指针有三个基本操作:获取地址、解引用和初始化。
1. 获取地址:使用&操作符获取变量的内存地址。
x := 10
p := &x // p存储了x的内存地址
fmt.Printf("x的值: %d\n", x) // 10
fmt.Printf("x的内存地址: %p\n", &x) // 0xc000018030 (地址值会不同)
fmt.Printf("p存储的地址: %p\n", p) // 0xc000018030 (与上面相同)
2. 解引用:使用*操作符访问指针指向的值。
*p = 20 // 通过指针修改x的值
fmt.Printf("修改后的x值: %d\n", x) // 20
fmt.Printf("通过指针读取x: %d\n", *p) // 20
3. 指针初始化:指针的零值是nil,表示不指向任何地址。
var p1 *int // 零值为nil
fmt.Println(p1) // <nil>
fmt.Println(p1 == nil) // true
// 初始化指针的几种方式
// 方式1:指向已有变量
x := 10
p2 := &x
// 方式2:使用new函数
p3 := new(int) // 分配内存并返回指针
*p3 = 100 // 设置值
1.3 Go指针的安全性
Go指针较C/C++更加安全,主要体现在以下限制:
-
没有指针算术:不能对指针进行加减运算。
x := [5]int{ 1, 2, 3, 4, 5} p := &x[0] // p++ // 编译错误:无效的操作 -
禁止指针类型转换:不能随意将一种类型的指针转换为另一种类型(除非使用
unsafe包)。f := 3.14 // ip := (*int)(&f) // 编译错误:非法类型转换 -
自动垃圾回收:不需要手动释放内存,避免了悬空指针和内存泄漏问题。
-
防止空指针解引用:对
nil指针的解引用会导致运行时panic,而不是未定义行为。var p *int // fmt.Println(*p) // 运行时错误:panic: runtime error: invalid memory address or nil pointer dereference
二、指针的应用
2.1 指针作为函数参数
在Go中,函数参数默认是值传递(复制)。使用指针作为参数可以实现引用传递的效果:
// 值传递:无法修改原始值
func increment(val int) {
val++
fmt.Println("函数内:", val) // val被修改
}
// 指针传递:可以修改原始值
func incrementPointer(val *int) {
*val++
fmt.Println("函数内:", *val) // 原始值被修改
}
func main() {
x := 10
increment(x)
fmt.Println("值传递后:", x) // x不变,仍为10
incrementPointer(&x)
fmt.Println("指针传递后:", x) // x变为11
}
何时使用指针作为函数参数?
- 需要在函数内修改传入变量的值时
- 传递大型结构体以避免复制开销时
- 传递切片、映射等引用类型时通常不需要使用指针(因为它们本身已经包含了指针)
2.2 结构体与指针
结构体与指针的结合使用非常常见:
type Person struct {
Name string
Age int
}
// 创建并返回结构体指针
func NewPerson(name string, age int) *Person {
return &Person{

最低0.47元/天 解锁文章
4万+

被折叠的 条评论
为什么被折叠?



