Storing a pointer to a stack value (Golang)

大憨熊 提交于 2021-01-27 06:33:12

问题


I'm trying an experiment in Go, to see what happens if I store a pointer to a variable on the stack, and then access that variable after the original variable has left scope.

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");
}

In alloc_on_stack, v1 is stored as a local variable on the stack. I pass a pointer to v1 to update_v, which passes it to another_thread. By another_thread doesn't execute until after alloc_on_stack finishes.

Yet, when I run that code, I don't get any errors, instead I see this:

alloc_on_stack(): 0x1043617c
update_v(): 0x1043617c
 -  3
outside of alloc_on_stack, waiting
another_thread(): 0x1043617c
 -  4
done

Shouldn't vx inside another_thread be a dangling pointer?


回答1:


Nope. The Go compiler detects that you are taking the address of a local variable, and keeps it around until all references to it are gone. From then on, the variable can be garbage collected.

This is why stuff like this isn't just allowed, it's even idiomatic:

func foo() *Bar {
  return &Bar{42, "frob"}
}



回答2:


Go doesn't distinguish between stack and heap as a language. Its implementations use escape analysis to prove that certain variables, even though they are referenced, can be placed on the stack safely. A naive implementation could just place all variables which are referenced in the heap.

You can use the -m flag on 6g to print out performance optimizations such as when it places something on the stack or heap.

Given your example:

$ 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


来源:https://stackoverflow.com/questions/28484976/storing-a-pointer-to-a-stack-value-golang

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!