0

0

Go语言中sync.WaitGroup指针地址的常见误解与解析

霞舞

霞舞

发布时间:2025-12-05 17:00:02

|

413人浏览过

|

来源于php中文网

原创

go语言中sync.waitgroup指针地址的常见误解与解析

本文深入解析Go语言中一个常见的指针使用误区,即混淆指针变量自身的内存地址与其所指向值的内存地址。通过sync.WaitGroup的实际案例,文章详细阐述了在不同作用域和打印操作下,如何正确理解和追踪指针变量的地址,旨在帮助开发者准确识别并避免在Go并发编程中关于指针地址的潜在错误。

Go语言中的指针基础

在Go语言中,指针是一种特殊的数据类型,它存储了另一个变量的内存地址。理解指针的核心在于区分“指针变量本身”和“指针变量所指向的值”。

  • & (取地址运算符):用于获取一个变量的内存地址。例如,&x 返回变量 x 的地址。
  • *`(解引用运算符)**:用于访问指针所指向的值。例如,*ptr返回指针ptr` 所指向的变量的值。
  • 指针变量类型:如果 x 是类型 T 的变量,那么 &x 的类型就是 *T,表示“指向类型 T 的指针”。

深入分析sync.WaitGroup地址追踪问题

考虑以下Go语言代码片段,它展示了在处理 sync.WaitGroup 时,关于指针地址打印的一个常见困惑:

package main

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

func Run() *sync.WaitGroup {
    var wg sync.WaitGroup // 局部变量wg
    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Printf("goroutine内部打印的Waitgroup地址: %p\n", &wg) // 打印wg的地址
        time.Sleep(5 * time.Second)
        fmt.Println("goroutine执行完毕")
    }()

    fmt.Printf("Run函数返回前打印的Waitgroup地址: %p\n", &wg) // 打印wg的地址
    return &wg // 返回wg的地址
}

func main() {
    runtime.GOMAXPROCS(3)
    wg := Run() // main函数中的wg是一个*sync.WaitGroup类型的指针变量

    fmt.Printf("main函数中打印的wg变量自身的地址: %p\n", &wg) // 打印main函数中wg变量的地址
    wg.Wait()
}

运行上述代码,可能会得到类似以下输出(地址值会因运行环境而异):

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

Run函数返回前打印的Waitgroup地址: 0xc0000120a0
main函数中打印的wg变量自身的地址: 0xc00000e028
goroutine内部打印的Waitgroup地址: 0xc0000120a0
goroutine执行完毕

观察输出,Run 函数内部和 goroutine 内部打印的 WaitGroup 地址是相同的,这符合预期,因为 goroutine 通过闭包捕获了 Run 函数中 wg 变量的地址。然而,main 函数中打印的地址却与前两者不同,这正是问题的核心所在。

核心概念辨析:指针变量的地址与指针指向的地址

造成上述差异的原因在于对 main 函数中 fmt.Printf(" main %p\n", &wg) 的误解。

  1. Run 函数内部 (&wg): 在 Run 函数中,var wg sync.WaitGroup 声明了一个 WaitGroup 类型的局部变量 wg。当执行 fmt.Printf("... %p\n", &wg) 时,它打印的是这个 WaitGroup 实例在内存中的实际地址。由于 Run 函数返回了 &wg,这个 wg 实例会发生逃逸(Escape to Heap),其生命周期延长到不再被引用为止。

  2. goroutine 内部 (&wg): goroutine 是一个匿名函数,它通过闭包捕获了外部 Run 函数作用域中的 wg 变量。因此,goroutine 内部打印的 &wg 依然是 Run 函数中那个 WaitGroup 实例的地址。

  3. main 函数内部 (wg := Run() 后,&wg):

    • wg := Run():Run 函数返回的是一个 *sync.WaitGroup 类型的值(即 Run 函数中 WaitGroup 实例的地址)。在 main 函数中,wg 被声明为一个 *sync.WaitGroup 类型的指针变量,它存储了 Run 函数返回的那个地址。
    • fmt.Printf("... %p\n", &wg):这里的 &wg 打印的不再是 WaitGroup 实例的地址,而是 main 函数中局部变量 wg 自身 的内存地址。这个 wg 变量是一个指针,它存储了另一个地址。你可以将 main 函数中的 wg 想象成一个信封,信封上写着“请寄到地址 XXXXX”,而 &wg 打印的是这个信封本身的地址,而不是信封上写的地址 XXXXX。

正确追踪sync.WaitGroup地址的实践

为了在 main 函数中打印 Run 函数返回的 WaitGroup 实例的实际地址,我们应该打印指针变量 wg 的 ,而不是 wg 变量 自身 的地址。因为指针变量的值就是它所指向的内存地址。

将 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内部打印的Waitgroup地址: %p\n", &wg)
        time.Sleep(5 * time.Second)
        fmt.Println("goroutine执行完毕")
    }()
    fmt.Printf("Run函数返回前打印的Waitgroup地址: %p\n", &wg)
    return &wg
}

func main() {
    runtime.GOMAXPROCS(3)
    wg := Run() // main函数中的wg是一个*sync.WaitGroup类型的指针变量

    // 修正:打印wg指针变量所指向的地址,而不是wg变量自身的地址
    fmt.Printf("main函数中打印的Waitgroup实际地址: %p\n", wg)
    wg.Wait()
}

运行修正后的代码,输出将显示所有打印的 WaitGroup 实例地址一致:

Run函数返回前打印的Waitgroup地址: 0xc0000120a0
main函数中打印的Waitgroup实际地址: 0xc0000120a0
goroutine内部打印的Waitgroup地址: 0xc0000120a0
goroutine执行完毕

注意事项与总结

  1. 区分 &ptrVar 和 ptrVar: 这是理解Go语言指针的关键。&ptrVar 给出的是存储指针的变量本身的地址,而 ptrVar 给出的是该指针变量所存储的地址(即它指向的值的地址)。
  2. 并发编程中的指针: 在并发编程中,如 sync.WaitGroup、sync.Mutex 等同步原语,通常需要通过指针传递,以确保所有并发协程操作的是同一个实例。正确理解和追踪这些共享资源的地址至关重要,以避免逻辑错误。
  3. 栈逃逸: 尽管 Run 函数中的 wg 是局部变量,但因为它被函数返回了其地址,Go编译器会将其从栈上分配到堆上(即发生栈逃逸),以确保其在 Run 函数返回后依然有效。这对于本问题中地址一致性是必要的,但不是造成地址差异的直接原因。

通过本文的详细解析,希望读者能够清晰地辨别Go语言中指针变量自身地址与其指向值地址的区别,从而在日常开发中,特别是在处理并发和共享数据时,更准确、更自信地运用指针。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

309

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

222

2025.10.31

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1499

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

231

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

87

2025.10.17

printf用法大全
printf用法大全

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

74

2023.06.20

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

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

283

2023.11.28

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

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

395

2023.07.18

Python 自然语言处理(NLP)基础与实战
Python 自然语言处理(NLP)基础与实战

本专题系统讲解 Python 在自然语言处理(NLP)领域的基础方法与实战应用,涵盖文本预处理(分词、去停用词)、词性标注、命名实体识别、关键词提取、情感分析,以及常用 NLP 库(NLTK、spaCy)的核心用法。通过真实文本案例,帮助学习者掌握 使用 Python 进行文本分析与语言数据处理的完整流程,适用于内容分析、舆情监测与智能文本应用场景。

10

2026.01.27

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 4.3万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号