存储指向堆栈值的指针(Golang)

And*_*ice 3 pointers go

我正在尝试Go中的实验,看看如果我将指针存储到堆栈上的变量会发生什么,然后在原始变量离开作用域后访问该变量.

package main

import "fmt"

var p chan bool;

// a temp struct
type v struct {
    a int
}

func another_thread(vx *v) {
    // this code should be executed after a() returns so vx should be a pointer to a value that's no longer on the stack
    fmt.Printf("another_thread(): %p\n", vx);
    vx.a = 4 // am I updating a dangling pointer that may have unintentional side effects??
    fmt.Println(" - ", vx.a);
    p<-true;
}

func update_v(vx *v) {
    vx.a = 3;

    fmt.Printf("update_v(): %p\n", vx);

    go another_thread(vx)
}

func alloc_on_stack() {
    // allocate v1 on the stack
    var v1 v
    v1.a = 1

    fmt.Printf("alloc_on_stack(): %p\n", &v1);

    // pass a pointer to v1 on the stack
    update_v(&v1)

    // print '3' to prove byref actually took it by reference
    fmt.Println(" - ", v1.a);

    // when the function returns, v1 should be popped off the stack
}

func main() {
    p = make(chan bool)
    alloc_on_stack();
    fmt.Println("outside of alloc_on_stack, waiting");
    <-p;
    fmt.Println("done");
}
Run Code Online (Sandbox Code Playgroud)

在alloc_on_stack中,v1作为局部变量存储在堆栈中.我将指向v1的指针传递给update_v,并将其传递给another_thread.在alloc_on_stack完成之后,another_thread才会执行.

然而,当我运行该代码时,我没有得到任何错误,而是我看到了这个:

alloc_on_stack(): 0x1043617c
update_v(): 0x1043617c
 -  3
outside of alloc_on_stack, waiting
another_thread(): 0x1043617c
 -  4
done
Run Code Online (Sandbox Code Playgroud)

不应该在another_thread里面的vx是悬空指针吗?

Tho*_*mas 8

不.Go编译器检测到您正在获取局部变量的地址,并将其保留,直到所有对它的引用都消失为止.从那时起,变量可以被垃圾收集.

这就是为什么这样的东西不仅仅是允许的,它甚至是惯用的:

func foo() *Bar {
  return &Bar{42, "frob"}
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*erg 6

Go不区分堆栈和堆作为语言.它的实现使用转义分析来证明某些变量,即使它们被引用,也可以安全地放在堆栈中.一个简单的实现可以放置堆中引用的所有变量.

您可以在6g上使用-m标志来打印性能优化,例如何时将某些内容放在堆栈或堆上.

举个例子:

$ go build -gcflags "-m" tmp.go 
# command-line-arguments
./tmp.go:12: leaking param: vx
./tmp.go:14: another_thread ... argument does not escape
./tmp.go:16: another_thread ... argument does not escape
./tmp.go:20: leaking param: vx
./tmp.go:20: leaking param: vx
./tmp.go:20: leaking param: vx
./tmp.go:23: update_v ... argument does not escape
./tmp.go:30: moved to heap: v1
./tmp.go:33: &v1 escapes to heap
./tmp.go:36: &v1 escapes to heap
./tmp.go:33: alloc_on_stack ... argument does not escape
./tmp.go:39: alloc_on_stack ... argument does not escape
./tmp.go:45: make(chan bool, 0) escapes to heap
./tmp.go:47: main ... argument does not escape
./tmp.go:49: main ... argument does not escape
Run Code Online (Sandbox Code Playgroud)