新闻中心

深入理解Go语言中sync.WaitGroup指针地址的打印行为

2025-12-05
浏览次数:
返回列表

深入理解go语言中sync.waitgroup指针地址的打印行为

本文旨在阐明Go语言中打印`sync.WaitGroup`指针地址时常见的混淆点,特别是当一个函数返回`*sync.WaitGroup`类型时。我们将通过代码示例详细解析`&wg`和`wg`在不同作用域中的含义,帮助开发者区分变量本身的地址与变量所存储的指针值,从而避免因误解而产生的地址不一致现象。

理解Go语言中指针地址的打印行为

在Go语言中,理解变量、指针以及它们在不同作用域中的行为至关重要,尤其是在处理并发原语如sync.WaitGroup时。开发者有时会发现,当一个函数返回*sync.WaitGroup后,在调用方打印该指针的地址与在函数内部打印的地址不一致,这通常是由于对Go语言中&操作符和变量本身含义的混淆所致。

初始问题场景

考虑以下Go语言代码片段,它尝试使用sync.WaitGroup来协调主协程与一个子协程的执行,并打印WaitGroup的内存地址:

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

func Run() *sync.WaitGroup {
    var wg sync.WaitGroup // 声明一个 WaitGroup 实例
    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Printf("goroutine %p\n", &wg) // 打印 wg 实例的地址
        time.Sleep(5 * time.Second)
        fmt.Println("wokeup")
    }()

    fmt.Printf("returning %p\n", &wg) // 打印 wg 实例的地址
    return &wg                       // 返回 wg 实例的地址
}

func main() {
    runtime.GOMAXPROCS(3)
    wg := Run() // 接收 Run 函数返回的 *sync.WaitGroup
    fmt.Printf("     main %p\n", &wg) // 打印 wg 变量的地址
    wg.Wait()
}

运行上述代码,可能会得到类似以下的输出:

returning 0xc00000e000
     main 0xc000006020
goroutine 0xc00000e000
wokeup

从输出中可以看到,Run函数内部和goroutine中打印的地址(0xc00000e000)是一致的,但main函数中打印的地址(0xc000006020)却不同。这似乎与预期不符,因为我们期望在main函数中得到的WaitGroup指针地址应该与Run函数中返回的地址相同。

问题分析:&操作符的含义

问题的核心在于对&操作符的理解以及变量在不同作用域中的表示。

  1. 在Run函数内部 (Run和goroutine):

    • var wg sync.WaitGroup:这里声明了一个sync.WaitGroup类型的变量wg。它是一个具体的结构体实例,存储在内存中的某个位置。
    • &wg:这个表达式获取的是wg这个结构体实例本身的内存地址。无论是在Run函数的主体中还是在它启动的goroutine中,wg都指向同一个内存中的WaitGroup实例。因此,returning %p和goroutine %p打印的地址是相同的,因为它们都指向同一个WaitGroup对象的物理地址。
    • return &wg:Run函数返回的正是这个WaitGroup实例的内存地址,即一个*sync.WaitGroup类型的值。
  2. 在main函数内部:

    Lateral App Lateral App

    整理归类论文

    Lateral App 85 查看详情 Lateral App
    • wg := Run():main函数声明了一个新的局部变量,也命名为wg。这个wg变量的类型是*sync.WaitGroup,它存储了Run函数返回的那个WaitGroup实例的内存地址
    • fmt.Printf(" main %p\n", &wg):这里的&wg不再是WaitGroup实例的地址。相反,它获取的是main函数中局部变量wg本身的内存地址。由于main函数中的wg是一个局部变量,它在main函数的栈帧中分配内存,用于存储从Run函数返回的指针值。因此,&wg打印的是这个存储指针值的变量的地址,而不是它所指向的WaitGroup实例的地址。

简单来说,Run函数返回的是一个门牌号(WaitGroup实例的地址),而main函数中的wg变量是一个信箱,里面存放着这个门牌号。&wg在main函数中打印的是信箱本身的地址,而不是信箱里存放的门牌号。

解决方案

要使main函数中打印的地址与Run函数中返回的WaitGroup实例地址一致,我们应该打印main函数中wg变量所存储的值,而不是wg变量本身的地址。由于wg在main函数中已经是一个*sync.WaitGroup类型的指针,其值就是WaitGroup实例的地址。

因此,只需将main函数中的打印语句从fmt.Printf(" main %p\n", &wg)修改为fmt.Printf(" main %p\n", wg)。

修正后的代码示例

package main

import (
    "fmt"
    "runtime"
    "sync"
    "time"
)

func Run() *sync.WaitGroup {
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Printf("goroutine %p\n", &wg) // 打印 wg 实例的地址
        fmt.Println("sleep for 5s")
        time.Sleep(5 * time.Second)
        fmt.Println("wokeup")
    }()
    fmt.Printf("returning %p\n", &wg) // 打印 wg 实例的地址
    return &wg                       // 返回 wg 实例的地址
}

func main() {
    runtime.GOMAXPROCS(3)
    wg := Run()
    fmt.Printf("     main %p\n", wg) // **修正点:打印 wg 变量存储的指针值**
    wg.Wait()
}

修正后的代码运行输出如下:

returning 0x1052e2c0
     main 0x1052e2c0
goroutine 0x1052e2c0
sleep for 5s

现在,所有打印的地址都一致了,它们都指向了同一个sync.WaitGroup实例的内存地址。

总结与注意事项

  • &variable vs. variable (当variable是指针时):
    • &variable:总是获取variable这个变量本身在内存中的地址。
    • variable (当variable是指针类型时):表示variable所存储的值,这个值就是一个内存地址(即它所指向的对象的地址)。
  • 在Go语言中,当一个函数返回一个指针(如*sync.WaitGroup)时,接收这个返回值的变量(如main函数中的wg)其类型就是指针类型。此时,直接打印该变量(fmt.Printf("%p", wg))会显示它所指向的对象的地址。
  • 如果想打印存储这个指针的变量本身的地址,才需要使用&操作符(fmt.Printf("%p", &wg))。
  • 理解这一区别对于调试内存问题、理解并发原语的工作方式以及编写健壮的Go程序至关重要。

通过区分变量本身的地址和变量所存储的指针值,我们可以准确地追踪Go程序中对象的内存位置,从而避免常见的混淆和错误。

以上就是深入理解Go语言中sync.WaitGroup指针地址的打印行为的详细内容,更多请关注其它相关文章!


# go语言  # google营销推广怎么做  # 集美厦门网站建设公司  # 阿坝网站推广优化  # 海口建设网站公司  # 我们可以  # 它是  # 只需  # 这一  # 至关重要  # 一个函数  # 而不是  # 是在  # 是一个  # 的是  # 作用域  # 区别  # ai  #   # go  # 贴吧视频营销推广  # 盐城企业网站建设案例  # 黄岛网站建设费用  # 临沂网站建设网站推广  # 同城引流获客推广的营销 


相关栏目: 【 科技资讯46185 】 【 网络学院92790


相关推荐: 漫蛙网页登录入口 漫蛙漫画官方授权网址  c++中的std::launder有什么实际用途_c++对象生命周期与指针优化  抖音DOU+怎么投最有效 抖音付费推广的ROI提升技巧  html怎么在cmd下运行php文件_cmd运行html中php文件方法【教程】  UC浏览器官网入口2025最新 UC浏览器网页版正式地址  Python模块化编程:有效管理依赖与避免循环引用  2306选座时如何选靠窗位置_12306选座靠窗座位查看方法解析  Windows7怎么硬盘安装 Windows7提取ISO镜像到非系统盘并运行setup.exe实现硬盘直装【教程】  C#中解析不规范的HTML为XML 常见的坑与解决办法  Excel文件在线转换快速入口 Excel在线格式转换网站  绝地鸭卫平a核爆刀流玩法攻略  Lar*el如何生成PDF或Excel文件_Lar*el文档导出工具与使用教程  AWS EC2实例间SQL Server连接超时:安全组配置与故障排除指南  钉钉视频会议声音异常如何处理 钉钉会议音频修复技巧  win11 Snap Layouts怎么用 Win11窗口布局与分屏多任务高效指南【必学】  PHP中高效并行检查多链接状态的教程  J*a TimerTask中HashMap意外清空的深层原因与解决方案  在J*a中如何开发简易仓库管理与库存统计_仓库管理库存统计项目实战解析  html5 app怎么运行环境_配html5 app运行环境【教程】  荒野行动PC版怎么注册_荒野行动PC版账号注册详细流程图文教程  包子漫画官方网站阅读入口-包子漫画在线漫画官网直达链接  没有大陆身份证/银行卡如何实名微信? 亲测有效的几种方法分享  Go语言中JSON数据解析与字段访问教程  Win11如何使用Windows Sandbox Win11沙盒功能开启与使用教程【详解】  taptap防沉迷怎么解除 taptap解除健康系统限制说明【2025最新】  Python中如何避免重复条件判断:利用数据结构实现动态逻辑  优化 Python 函数中的条件逻辑:解决 if-else 嵌套与参数选择问题  PPT平滑切换怎么做 PPT炫酷“平滑”切换动画制作教程【必学】  快手极速版在线观看 官方网页版登录地址  python3时间如何用calendar输出?  护手霜蹭到袖口上了如何清洗? 怎样避免留下一圈油印?  wps文字怎么插入目录并自动更新_wps文字如何插入目录并自动更新方法  QQ邮箱网页版入口登录 QQ邮箱在线邮箱官方通道  LINUX下如何进行磁盘分区_fdisk与parted工具在LINUX中的使用对比  C++如何生成随机数_C++ random库使用方法与范围设置  如何在 Excel Online 和 Google 表格中更改日期格式  腾讯QQ邮箱官方网站_QQ邮箱网页版在线登录  QQ邮箱在线登录平台 QQ邮箱个人邮箱网页版入口  如何使用CaptainHook和Composer管理Git钩子_在提交前自动运行代码检查的Composer配置  电脑安装程序提示“错误1722”怎么办_Windows Installer服务问题解决【教程】  理解J*aScript Promise的微任务队列与执行顺序  Yandex免登录官网入口_俄罗斯Yandex搜索引擎直达链接  C++如何比较两个字符串_C++ string compare函数与操作符对比  Golang如何测试channel通信行为_Golang channel通信测试与分析方法  响应式图片在网页设计中的正确实现方法  荣耀Play7T运行卡顿解决_荣耀Play7T性能优化  AO3官方可用镜像 Archive of Our Own网页版最新入口  qq游戏网页版直接玩_qq游戏免下载快速入口  Safari怎么安装扩展程序 浏览器插件安装与管理方法【详解】  微信语音通话掉线如何解决 微信语音通话稳定优化方法 

搜索