
本文深入解析Go语言中一个常见的指针使用误区,即混淆指针变量自身的内存地址与其所指向值的内存地址。通过sync.WaitGroup的实际案例,文章详细阐述了在不同作用域和打印操作下,如何正确理解和追踪指针变量的地址,旨在帮助开发者准确识别并避免在Go并发编程中关于指针地址的潜在错误。
在Go语言中,指针是一种特殊的数据类型,它存储了另一个变量的内存地址。理解指针的核心在于区分“指针变量本身”和“指针变量所指向的值”。
考虑以下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) 的误解。
Run 函数内部 (&wg): 在 Run 函数中,var wg sync.WaitGroup 声明了一个 WaitGroup 类型的局部变量 wg。当执行 fmt.Printf("... %p\n", &wg) 时,它打印的是这个 WaitGroup 实例在内存中的实际地址。由于 Run 函数返回了 &wg,这个 wg 实例会发生栈逃逸(Escape to Heap),其生命周期延长到不再被引用为止。
goroutine 内部 (&wg): goroutine 是一个匿名函数,它通过闭包捕获了外部 Run 函数作用域中的 wg 变量。因此,goroutine 内部打印的 &wg 依然是 Run 函数中那个 WaitGroup 实例的地址。
main 函数内部 (wg := Run() 后,&wg):
为了在 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执行完毕
通过本文的详细解析,希望读者能够清晰地辨别Go语言中指针变量自身地址与其指向值地址的区别,从而在日常开发中,特别是在处理并发和共享数据时,更准确、更自信地运用指针。
以上就是Go语言中sync.WaitGroup指针地址的常见误解与解析的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号