【Go语言学习系列09】Go基础语法(七):指针

📚 原创系列: “Go语言学习系列”

🔄 转载说明: 本文最初发布于"Gopher部落"微信公众号,经原作者授权转载。

🔗 关注原创: 欢迎扫描文末二维码,关注"Gopher部落"微信公众号获取第一手Go技术文章。

📑 Go语言学习系列导航

本文是【Go语言学习系列】的第9篇,当前位于第一阶段(入门篇)

🚀 第一阶段:入门篇
  1. Go语言简介与环境搭建
  2. Go开发工具链介绍
  3. Go基础语法(一):变量与数据类型
  4. Go基础语法(二):流程控制
  5. Go基础语法(三):函数
  6. Go基础语法(四):数组与切片
  7. Go基础语法(五):映射
  8. Go基础语法(六):结构体
  9. Go基础语法(七):指针 👈 当前位置
  10. Go基础语法(八):接口
  11. 错误处理与异常
  12. 第一阶段项目实战:命令行工具

📚 查看完整Go语言学习系列导航

📖 文章导读

在本文中,您将学习:

  • 指针的基本概念与内存模型
  • Go指针的安全特性与C/C++指针的区别
  • 指针的声明、初始化与操作方法
  • 函数参数中值传递与指针传递的应用场景
  • 结构体与指针的结合使用技巧
  • 常见的指针陷阱与解决方案
  • unsafe包的高级应用与注意事项

指针是Go语言中非常重要的特性,它既提供了直接操作内存的能力,又通过严格的安全限制避免了常见的内存问题。本文将帮助您全面掌握指针的使用,提升代码性能,同时避免常见陷阱。

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++更加安全,主要体现在以下限制:

  1. 没有指针算术:不能对指针进行加减运算。

    x := [5]int{
         
         1, 2, 3, 4, 5}
    p := &x[0]
    // p++ // 编译错误:无效的操作
    
  2. 禁止指针类型转换:不能随意将一种类型的指针转换为另一种类型(除非使用unsafe包)。

    f := 3.14
    // ip := (*int)(&f) // 编译错误:非法类型转换
    
  3. 自动垃圾回收:不需要手动释放内存,避免了悬空指针和内存泄漏问题。

  4. 防止空指针解引用:对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
}

何时使用指针作为函数参数?

  1. 需要在函数内修改传入变量的值时
  2. 传递大型结构体以避免复制开销时
  3. 传递切片、映射等引用类型时通常不需要使用指针(因为它们本身已经包含了指针)

2.2 结构体与指针

结构体与指针的结合使用非常常见:

type Person struct {
   
   
    Name string
    Age  int
}

// 创建并返回结构体指针
func NewPerson(name string, age int) *Person {
   
   
    return &Person{
   
   
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gopher部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值