当前位置: 首页 > news >正文

Golang 指针

Golang 指针

一、先明确:什么是「内存地址」?

计算机的内存像无数个「小盒子」,每个盒子有唯一编号(即内存地址),用于存放数据。

  • 变量本质是给某个「小盒子」起的名字,比如 a := 10 中,a 就是编号为 0xc00001a0a8(假设)的盒子的名字,盒子里装着 10
  • 「地址」就是这个盒子的编号(如 0xc00001a0a8),「指针」就是专门用来存储这个编号的变量。

二、&:取地址运算符——把变量的「盒子编号」取出来

& 的作用是获取变量对应的内存地址,返回值是一个「指针」(即地址本身)。

核心特性:
  • 操作对象:必须是「变量」(不能对常量、表达式用 &,比如 &10&(a+b) 是错误的)。
  • 返回结果:类型为「*T」(T 是原变量的类型),表示「指向 T 类型的指针」。
示例:
package mainimport "fmt"func main() {a := 10      // 变量a,存着10,地址假设为0xc00001a0a8ptr := &a    // 用&取a的地址,ptr就是指针(类型为*int)fmt.Println(a)   // 输出:10(a盒子里的内容)fmt.Println(&a)  // 输出:0xc00001a0a8(a的地址,即ptr的值)fmt.Printf("ptr的类型:%T\n", ptr)  // 输出:*int(指向int的指针)
}
类比理解:
  • 你家地址是「北京市朝阳区XX街100号」(对应 &a)。
  • 你本人是 a&a 就是你家的门牌号。

三、*:双重身份——定义指针类型 + 解引用指针

* 有两个完全不同的用法,需根据上下文区分:

1. 定义指针类型(声明变量时用)

在类型前加 *,表示「这个变量是指针,专门存另一个变量的地址」。

格式:
var 指针变量名 *目标类型  // 声明一个指向「目标类型」的指针
示例:
var p *int    // p是指针变量,只能存int类型变量的地址(或nil)
var s *string // s是指针变量,只能存string类型变量的地址(或nil)
注意:
  • 未初始化的指针默认值为 nil(表示不指向任何地址,类似「空门牌号」)。
  • 指针类型严格匹配:*int 不能存 string 变量的地址,就像「只能存北京地址的本子,不能写上海地址」。
2. 解引用指针(使用指针时用)

对指针变量用 *,表示「根据指针存的地址,找到对应的变量,获取/修改它的值」。

核心作用:

通过指针间接操作目标变量(不用直接用变量名)。

示例1:获取指针指向的值
a := 10
p := &a  // p存a的地址(0xc00001a0a8)fmt.Println(*p)  // 输出:10(通过p的地址找到a,取出值)
示例2:通过指针修改目标变量的值
a := 10
p := &a*p = 20  // 解引用p,找到a,把a的值改成20
fmt.Println(a)  // 输出:20(a被修改)
类比理解:
  • p 是一张纸条,上面写着你家地址(&a)。
  • *p 就是「根据纸条上的地址找到你家,然后敲门看看里面有什么(取值),或者把里面的东西换了(修改值)」。

四、&* 的「互逆关系」

对变量 x 来说:

  • &x 得到指针 pp = &x);
  • *p 得到原变量 x*p = x)。
验证示例:
x := 5
p := &x  // p = &x(p是x的地址)
fmt.Println(*p == x)  // 输出:true(*p 等于 x的值)
注意:

&*p 等价于 p(先解引用 p 得到 x,再取 x 的地址,结果还是 p);
*&x 等价于 x(先取 x 的地址得到 p,再解引用 p,结果还是 x)。

五、指针的实际用途:解决「值传递」的局限

Go 语言函数参数默认是「值传递」(传变量的副本),如果想在函数内修改外部变量,必须用指针。

反例:值传递无法修改外部变量
func modify(a int) {a = 100  // 这里修改的是a的副本,和外部变量无关
}func main() {x := 10modify(x)fmt.Println(x)  // 输出:10(x没被修改)
}
正例:用指针传递实现修改
func modify(p *int) {*p = 100  // 解引用p,修改外部变量x的值
}func main() {x := 10modify(&x)  // 传x的地址(指针)fmt.Println(x)  // 输出:100(x被成功修改)
}
结构体场景:高效传递大对象

结构体可能包含大量字段(比如100个字段),值传递会复制整个结构体,效率低;而指针传递只复制一个地址(8字节,64位系统),更高效。

type Person struct {name stringage  int// ... 很多字段
}// 指针传递:只传地址,高效
func updateName(p *Person) {p.name = "张三"  // 直接修改原结构体
}func main() {p := Person{name: "李四", age: 20}updateName(&p)   // 传p的地址fmt.Println(p.name)  // 输出:张三(原结构体被修改)
}

六、new() 函数:另一种创建指针的方式

new(T) 会创建一个 T 类型的变量,初始化默认值(如 int 为0,string 为空),并返回该变量的指针(*T 类型)。

用法对比:
// 方法1:先定义变量,再取地址
var a int
p1 := &a  // p1是*int类型,指向a(a的默认值为0)// 方法2:用new()直接创建指针
p2 := new(int)  // p2是*int类型,指向一个默认值为0的int变量
*p2 = 10        // 给指针指向的变量赋值fmt.Println(*p1)  // 输出:0
fmt.Println(*p2)  // 输出:10
注意:

new() 只用于创建「基本类型」或「结构体」的指针,不能直接初始化复杂数据(如切片、映射,它们有专门的初始化函数)。

七、常见误区和注意事项

  1. 对 nil 指针解引用会崩溃
    nil 指针不指向任何地址,对它用 * 会触发运行时错误:

    var p *int  // p是nil
    *p = 10     // 报错:panic: runtime error: invalid memory address or nil pointer dereference
    
  2. 指针也有自己的地址
    指针本身也是变量,存储在内存中,所以 &p 是「指针的地址」(类型为 **int,即「指向指针的指针」):

    a := 10
    p := &a       // p是*int类型,值为&a
    pp := &p      // pp是**int类型,值为&p
    fmt.Println(*pp == p)  // 输出:true(解引用pp得到p)
    fmt.Println(** pp == a) // 输出:true(连续解引用得到a)
    
  3. 不要过度使用指针
    指针会增加代码复杂度,能不用就不用:

    • 基本类型(int、string等)传值效率很高,无需指针;
    • 函数内不需要修改外部变量时,用值传递更清晰。

总结:核心公式

运算符作用场景示例类比
&x取变量x的地址,得指针p := &a问「a住在哪」,得到地址纸条p
*p解引用指针p,得目标值fmt.Println(*p)按纸条p的地址找过去,看里面有什么
*p = v通过指针修改目标值*p = 20按纸条p的地址找过去,把里面的东西换成v
*T定义指向T类型的指针类型var p *int声明一个「只能写int类型地址」的纸条本

理解指针的关键:指针是「地址的载体」,& 负责生成这个载体,* 负责通过载体操作目标。多写几个修改变量、传递结构体的例子,很快就能熟练掌握~

http://www.dtcms.com/a/307540.html

相关文章:

  • Valgrind终极指南:深入内存安全与性能瓶颈检测
  • 云原生运维与混合云运维:如何选择及 Wisdom SSH 的应用
  • Android依赖注入框架Hilt入门指南
  • 大白话畅谈:stm32中断和FreeRTOS的中断
  • 【源力觉醒 创作者计划】_巅峰对话文心 4.5 与通义千问、DeepSeek 能力对比解析
  • 【工具】NVM完全指南:Node.js版本管理工具的安装与使用详解
  • 如何将照片从 realme 手机传输到电脑?
  • MongoDB系列教程-第四章:MongoDB Compass可视化和管理MongoDB数据库
  • node.js之Koa框架
  • 蓝牙 BR/EDR 与 BLE PHY
  • Kafka在Springboot项目中的实践
  • vue3.0 + TypeScript 中使用 axios 同时进行二次封装
  • ESXI虚拟交换机 + H3C S5120交换机 + GR5200路由器组网笔记
  • 数据结构与算法:队列的表示和操作的实现
  • Linux 下自动化脚本安装Jdk、Nginx等软件
  • Java语言/Netty框架的新能源汽车充电桩系统平台
  • 《人工智能导论》(python版)第2章 python基础2.2编程基础
  • Rust视频处理开源项目精选
  • FFmpegHandler 功能解析,C语言程序化设计与C++面向对象设计的核心差异
  • 【日常问题解决方案】VS2022不小心解决方案资源管理器把关掉了怎么办
  • spring cloud alibaba ——gateway网关
  • Day36| 1049. 最后一块石头的重量 II、494.目标和、474.一和零
  • 图论-最短路Dijkstra算法
  • 澳交所技术重构窗口开启,中资科技企业如何破局?——从ASX清算系统转型看跨境金融基础设施的赋能路径
  • Python爬虫07_Requests爬取图片
  • 基于Spring Boot实现中医医学处方管理实践
  • 【05】大恒相机SDK C#开发 —— Winform中采集图像并显示
  • 金融分类提示词演示
  • 【03】大恒相机SDK C#开发 —— 回调采集图像,关闭相机
  • STM32学习记录--Day4