首页
学习
活动
专区
圈层
工具
发布

Go指针难哭新手?搞懂&和*这俩符号,10分钟入门!

学Go语言的你,是不是一碰到“指针”就头大?

看着代码里的&和*,一会儿取地址一会儿解引用,总觉得抽象又绕,甚至怕写错不敢用——明明知道指针很重要,却卡在“这俩符号到底啥意思”上。

其实真不用怕!指针的核心就是“记地址”和“找东西”,而&和*就是干这两件事的工具。今天用生活例子+极简代码,帮你10分钟搞懂它们,以后写指针再也不慌!

先搞懂:指针到底是啥?用生活例子说透

别被“指针”这俩字吓住,它其实很具象——就像你生活里记门牌号的纸条。

举个例子:

• 你有个变量a,值是10,它存在电脑内存里的某个“房间”里,这个房间有个唯一的“门牌号”(比如0xc00001a0a8,这就是内存地址);

• 指针变量p,它不存10这种具体值,而是存a的“门牌号”(0xc00001a0a8);

• 有了p,你就知道a住在哪个“房间”,能随时找到它、甚至修改它——这就是指针的作用:通过地址间接操作变量

而&和*,就是帮你“获取门牌号”和“按门牌号找东西”的两个工具,分工特别明确。

第一个符号:&(取地址符)——“抄门牌号”

&的作用只有一个:获取变量的内存地址(也就是抄下变量的“门牌号”)。

生活类比

你朋友住在“幸福路123号”(变量a的内存地址),你想记下来,就拿出纸条抄下这个地址——&a就是“抄门牌号”的动作。

极简代码示例(可直接复制运行)

package main

import "fmt"

func main() {

  // 1. 定义普通变量a,值是10(相当于“朋友住在某个房间,房间里有东西10”)

  a := 10

  // 2. 打印a的值(看房间里的东西)

  fmt.Println("变量a的值:", a)  // 输出:变量a的值:10

  // 3. 用&取a的内存地址(抄门牌号)

  fmt.Println("变量a的内存地址(门牌号):", &a)  // 输出类似:变量a的内存地址(门牌号):0xc00001a0a8

}关键结论

•&必须跟在“变量”后面,比如&a、&b,不能跟数字(比如&10会报错);

• 每次运行程序,内存地址可能不一样(就像每次换房子门牌号变了),但不影响使用;

• 用&得到的“地址”,可以赋值给一个“指针变量”(就像把抄好的门牌号贴在小本子上)。

第二个符号:*(两种用法,别搞混!)

*比&稍微复杂一点,因为它有两个完全不同的作用——但只要结合场景,一眼就能分清。

用法1:声明指针类型——“声明‘记门牌号的小本子’”

当*跟在“数据类型”前面时,作用是声明一个“指针变量”(也就是告诉Go:“我要一个小本子,专门记某类变量的门牌号”)。

生活类比

你说“我要一个专门记‘居民楼房间号’的小本子”——var p *int就是“我要一个专门记int类型变量地址的指针变量”。

代码示例

package main

import "fmt"

func main() {

  a := 10

  // 1. 用*int声明指针变量p(“小本子p,专门记int类型的门牌号”)

  // 刚声明时p是空的(nil),就像小本子还没写门牌号

  var p *int

  // 2. 把a的地址(&a)赋值给p(在小本子上写下a的门牌号)

  p = &a

  // 3. 打印p的值(看小本子上的门牌号)

  fmt.Println("指针p存储的地址:", p)  // 输出和&a一样,比如:0xc00001a0a8

  // 打印&a验证

  fmt.Println("变量a的地址(&a):", &a)  // 输出和p相同,说明p确实存了a的地址

}用法2:指针解引用——“按门牌号找房间里的东西”

当*跟在“指针变量”前面时,作用是获取指针指向的变量的值(也就是按小本子上的门牌号,找到房间里的东西),这个动作叫“解引用”。

更厉害的是:你还能通过*指针变量修改房间里的东西!

生活类比

你看小本子上的门牌号“幸福路123号”,去这个房间把里面的“10”换成“20”——*p = 20就是这个动作。

代码示例(核心用法,必看!)

package main

import "fmt"

func main() {

  a := 10

  // 1. 让指针p指向a(小本子记下a的门牌号)

  p := &a  // 这里是“&取地址”+“指针变量赋值”,一步到位

  // 2. 解引用:用*p获取a的值(按门牌号找东西)

  fmt.Println("通过*p获取a的值:", *p)  // 输出:通过*p获取a的值:10

  // 3. 解引用:用*p修改a的值(按门牌号改东西)

  *p = 20  // 相当于“去a的房间,把10改成20”

  // 4. 打印a的值,看是否被修改

  fmt.Println("修改后a的值:", a)  // 输出:修改后a的值:20

}关键结论

• 记准场景:*在“类型前”是声明指针(比如*int),在“指针变量前”是解引用(比如*p);

• 解引用的核心:通过指针“间接操作”目标变量,不用直接碰a,也能改a的值。

新手必看:指针的核心使用场景(函数参数传递)

光懂符号还不够,得知道指针在实际代码里怎么用——最常见的场景就是“函数参数传递”。

Go语言默认是“值传递”:给函数传变量时,传的是“变量的副本”,函数里改副本,外面的原变量不变。如果想在函数里改外面的变量,就得传指针(传变量的地址)。

对比示例:值传递 vs 指针传递

package main

import "fmt"

// 场景1:值传递(参数是普通int,传的是副本)

func modify1(x int) {

  x = 100  // 只改了副本,原变量a不变

  fmt.Println("函数里modify1的x值:", x)  // 输出:函数里modify1的x值:100

}

// 场景2:指针传递(参数是*int,传的是地址)

func modify2(x *int) {

  *x = 100  // 解引用,改的是指针指向的原变量a

  fmt.Println("函数里modify2的*x值:", *x)  // 输出:函数里modify2的*x值:100

}

func main() {

  a := 10

  // 1. 调用值传递的函数

  modify1(a)

  fmt.Println("调用modify1后,原变量a的值:", a)  // 输出:10(没被改)

  fmt.Println("------------------------")

  // 2. 调用指针传递的函数(传a的地址&a)

  modify2(&a)

  fmt.Println("调用modify2后,原变量a的值:", a)  // 输出:100(被改了)

}新手提问:什么时候用指针传递?

• 想在函数里修改外部变量的值(比如上面的modify2);

• 变量很大(比如大数组、结构体),传副本浪费内存,传地址更高效(地址就8个字节,不管变量多大)。

避坑指南:新手最容易犯的3个错

1.空指针解引用(必报错)

声明了指针变量但没赋值(比如var p *int),就直接用*p,会报错——因为小本子上没门牌号,找不到房间。

解决:先让指针指向一个变量(比如p = &a),再解引用。

2.把*的两种用法搞混

比如写var p int*(错),正确是var p *int——*要跟在类型前面,不是后面。

3.给指针传错类型

比如var p *int,却给p赋值&"hello"(错)——p是记int地址的小本子,不能记字符串的地址。

一句话总结:&和*到底怎么用?

•&变量:抄变量的门牌号(取地址),返回一个指针;

•*类型:声明一个“记该类型门牌号的小本子”(指针变量);

•*指针变量:按小本子上的门牌号找东西(解引用),能读能改。

最后问一句:你之前学指针时,最搞不懂的是&还是*?评论区聊聊,有问题我帮你解答!

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OSSS2fMiq4GoAzuR9atvJpDQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券