为什么在变量超出作用域后立即取消引用悬空指针时,Go 不会抛出错误?

问题描述 投票:0回答:1

我正在学习 Go 的内存管理和垃圾收集,并且在尝试在指针指向的变量超出范围后取消引用指针时遇到了一些意外行为。

在Go中,我知道垃圾收集(GC)是自动的,并且指针有时可以引用不再有效的内存。我预计取消引用指向范围外变量的指针会引发错误,但似乎 Go 这样做时确实会引发错误

这是一个演示该问题的最小示例:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    var p *int
    {
        x := 42
        p = &x  // p points to x
        fmt.Println(*p) // Prints 42
    }

    // Force garbage collection (for testing)
    runtime.GC()

    // Dereferencing p here should cause an error (since x is out of scope)
    fmt.Println(*p) // This does NOT throw any error
}

我的期望:

  • 我预计在 x 超出范围的内部块之后,指针 p 将是一个“悬空指针”,并且在取消引用时会立即导致运行时错误。
  • 我预期的错误类似于:运行时:无效的内存地址或零指针取消引用。

实际发生了什么:

  • 即使在强制垃圾回收之后,程序在取消引用 *p 时也不会出现恐慌或抛出错误。即使 x 超出范围,打印的值仍然是 x 的地址。
go pointers memory-management garbage-collection runtime-error
1个回答
0
投票

简单的答案是:如果需要,

x
会转义到堆。

仅仅因为变量超出范围并不意味着指向它的所有指针都悬空。 Go 编译器会检测是否存在指向变量的指针,如果它认为对该变量的引用在作用域结束后仍存在,则会将该变量移动到堆中。

也就是说,在上面的情况下,

x
甚至有可能不会逃逸到堆,因为编译器可以检测到指向
x
的指针在作用域之后仍然存在,但在函数返回之后则不然。所以在这种情况下
x
仍然可以存在于堆栈中。

© www.soinside.com 2019 - 2024. All rights reserved.