讲师中心 微信公众号
AI工具推荐 视频效率加速

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

云静酱_7412

云静酱_7412

发布时间:2025-12-05 16:12:01

|

593人浏览过

|

来源于php中文网

原创

深入理解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()
}

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

立即学习“go语言免费学习笔记(深入)”;

returning 0xc00000e000
     main 0xc000006020
goroutine 0xc00000e000
wokeup

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

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

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

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

    Go语言(Golang)1.26.0
    Go语言(Golang)1.26.0

    Go语言(Golang)1.26.0版本官方下载,版本号 1.26.0,适合旧项目维护、兼容性测试和指定版本开发环境搭建。

    下载
    • 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函数内部:

    • 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程序中对象的内存位置,从而避免常见的混淆和错误。

相关文章

全能打印神器
全能打印神器

全能打印神器是一款非常好用的打印软件,可以在电脑、手机、平板电脑等设备上使用。支持无线打印和云打印,操作非常简单,使用起来也非常方便,有需要的小伙伴快来保存下载体验吧!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

相关专题

更多
printf用法大全
printf用法大全

php中文网为大家提供printf用法大全,以及其他printf函数的相关文章、相关下载资源以及各种相关课程,供大家免费下载体验。

888

2023.06.20

fprintf和printf的区别
fprintf和printf的区别

fprintf和printf的区别在于输出的目标不同,printf输出到标准输出流,而fprintf输出到指定的文件流。根据需要选择合适的函数来进行输出操作。更多关于fprintf和printf的相关文章详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1029

2023.11.28

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

5174

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

5351

2025.07.04

C++ 智能指针与现代内存管理
C++ 智能指针与现代内存管理

深入讲解 C++ 现代内存管理的核心工具——智能指针,涵盖 unique_ptr 独占所有权语义、shared_ptr 引用计数机制与循环引用问题、weak_ptr 弱引用的应用场景、make_unique/make_shared 工厂函数的性能优势、自定义删除器的编写、RAII 资源管理思想的实践,以及从裸指针迁移到智能指针的重构策略,帮助开发者编写安全无泄漏的现代 C++ 代码。

379

2026.04.23

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

6587

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

2728

2023.08.10

Go中Type关键字的用法
Go中Type关键字的用法

Go中Type关键字的用法有定义新的类型别名或者创建新的结构体类型。本专题为大家提供Go相关的文章、下载、课程内容,供大家免费下载体验。

3249

2023.09.06

DBeaver怎么导入数据
DBeaver怎么导入数据

DBeaver数据导入专题,整理Excel、CSV、SQL、TXT等常见文件的数据导入方法,涵盖目标数据库选择、数据表创建、字段映射、数据类型设置及导入参数配置等操作,帮助用户将本地文件中的数据写入数据库,并了解批量导入、重复数据处理和导入失败等常见问题的解决方法。

0

2026.11.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn