66105


これはSIGSEGVを出しませんが、なぜですか？

[code]

    package main
    
    import "fmt"
    
    func get_pointer() *int{
        var x int = 1
        fmt.Println(&x)
        return &x
    }
    
    func main() {
        xp := get_pointer()
        *xp = 100
        fmt.Println(xp)
        fmt.Println(*xp)
    }
    
[/code]

[code]

    0xc00002c008
    0xc00002c008
    100
    
[/code]


CC BY-SA 4.0
2020-04-29T09:16:26.547
66099
2020-05-04T03:33:00.183
2020-04-29T11:25:13.900
3060
37013
question
go
golangで関数内の自動変数のポインタを返却してしまった場合


(追記)metropolis
さんの回答等に示されているように、このご質問の場合、「変数がスタックではなくヒープ領域に確保されるため、危険な参照とはならない」と言うのが正解と思われます。ただ、「誤ったポインタを操作しても直ちにSIGSEGVになるとは限らない」と言う点は成り立ちますので、とりあえずこの回答はそのまま残しておきます。

* * *

残念ながら、誤ったポインターの使い方の全てが直ちにSIGSEGVのようなCPU例外を引き起こすとは限りません。

例のようにローカル変数(Go言語で「自動変数」と言う言い方があるのかどうかは確かめられませんでした)へのポインターを戻してしまうと、そのポインターは既に解放された後のスタック領域を指すわけですが、スタック領域は、あなたのプログラムから書き込み可能となるようメモリが割り当てられており、その領域を読み出したり、書き込んだりしても、メモリが割り当てられている限り、SIGSEGVは発生しません。

[code]

         スタック
    xp->|      |
        | :    | ↑スタックの伸びる方向
        |------| <- CPUのスタックポインタ、本来これより低位のメモリアドレスにはアクセスしてはいけない 
        |      |
        | :    |
    
[/code]

例えば、`xp`の指すアドレスに対応するメモリが仮想記憶管理の関係で解放されてしまっていたり、`xp`に書き込んだ時にその領域が別の用途に再利用されている場合などにはSIGSEGVが発生する可能性もあるのですが、そのような条件が成り立たない場合には不幸なことに「見かけ上動いているように見える」ことになります。

### _これはSIGSEGVを出しませんが、なぜですか？_

誤ったポインタの使い方をしても、必ずSIGSEGVが出るとは限りません。自明におかしい場合にはコンパイラが警告などを出すと思いますが、「動かしてみたが問題なく動いているように見える」ことで安心せずに、注意しないといけません。


CC BY-SA 4.0
2020-04-29T10:03:19.853
66103
2020-05-04T03:33:00.183
2020-05-04T03:33:00.183
13972
13972
66099
answer


変数 `x` がスタックではなくヒープ領域に確保されたためです。

[code]

    $ go run -gcflags '-m' main.go
    # command-line-arguments
    ./main.go:7:13: inlining call to fmt.Println
    ./main.go:14:13: inlining call to fmt.Println
    ./main.go:15:13: inlining call to fmt.Println
    => ./main.go:6:6: moved to heap: x <=
    ./main.go:7:13: []interface {} literal does not escape
    ./main.go:14:13: []interface {} literal does not escape
    ./main.go:15:14: *xp escapes to heap
    ./main.go:15:13: []interface {} literal does not escape
    <autogenerated>:1: .this does not escape
    0xc000014178
    0xc000014178
    100
    
[/code]

これは golang の escape analysis と呼ばれる機能です。興味を持たれましたら [Allocation efficiency in
high-performance Go services](https://segment.com/blog/allocation-efficiency-
in-high-performance-go-services/) の "Some Pointers" を参照してみて下さい。


CC BY-SA 4.0
2020-04-29T10:11:06.477
66105
2020-04-29T10:11:06.477
66099
answer


Go
ではポインタによってデータへの参照が関数スコープ外に漏れているかどうかコンパイラが解析しており（[エスケープ解析](https://ja.wikipedia.org/wiki/%E3%82%A8%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%97%E8%A7%A3%E6%9E%90)）、これに従ってデータをスタックに置くかヒープに置くか管理しています。このため
C などとは違い関数スコープを気にせずポインタを return して良いです。ヒープに置かれ使われなくなったデータは
[GC](https://ja.wikipedia.org/wiki/%E3%82%AC%E3%83%99%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3)
によって処理されます。

このことはたとえば [Go の FAQ](https://golang.org/doc/faq#stack_or_heap)
にも以下のように書かれています。

> **How do I know whether a variable is allocated on the heap or the stack?**
>
> From a correctness standpoint, you don't need to know. Each variable in Go
> exists as long as there are references to it. The storage location chosen by
> the implementation is irrelevant to the semantics of the language. (以下略)

（日本語訳）

> **変数がヒープにあるかスタックにあるか、どうすれば知れますか？**
>
> 正確性を期すならば、知る必要はありません。Go
> ではそれぞれの変数はそこへの参照がある限り存在します。保存場所が実装によってどう選ばれるかは言語の意味論とは関係しません。（以下略）


CC BY-SA 4.0
2020-04-29T10:16:28.320
66106
2020-04-29T11:58:53.557
2020-04-29T11:58:53.557
19110
19110
66099
answer