「スタック」と「ヒープ」という単語は、言語仕様のどこにも表示されないことに注意してください。あなたの質問は「...はスタックで宣言されています」と「...はヒープで宣言されています」と表現されていますが、Go宣言構文はスタックまたはヒープについて何も述べていないことに注意してください。
これにより、技術的には、すべての質問に対する回答が実装に依存するようになります。もちろん、実際には、スタック(ゴルーチンごとに!)とヒープがあり、スタックにあるものとヒープにあるものがあります。コンパイラが厳密なルール(「new
常にヒープに割り当てる」など)に従う場合もあれば、オブジェクトがスタックに存在できるかどうか、またはオブジェクトをヒープに割り当てる必要があるかどうかを判断するために「エスケープ分析」を行う場合もあります。
例2では、エスケープ分析により、構造体のエスケープへのポインターが表示されるため、コンパイラーは構造体を割り当てる必要があります。ただし、この場合、Goの現在の実装は厳格なルールに従っていると思います。つまり、アドレスが構造体のいずれかの部分から取得された場合、構造体はヒープ上に配置されます。
質問3については、用語について混乱するリスクがあります。Goのすべては値で渡され、参照で渡されることはありません。ここでは、ポインタ値を返しています。ポインタのポイントは何ですか?例の次の変更を検討してください。
type MyStructType struct{}
func myFunction1() (*MyStructType, error) {
var chunk *MyStructType = new(MyStructType)
// ...
return chunk, nil
}
func myFunction2() (MyStructType, error) {
var chunk MyStructType
// ...
return chunk, nil
}
type bigStruct struct {
lots [1e6]float64
}
func myFunction3() (bigStruct, error) {
var chunk bigStruct
// ...
return chunk, nil
}
myFunction2を変更して、構造体のアドレスではなく構造体を返すようにしました。myFunction1とmyFunction2のアセンブリ出力を今すぐ比較します。
--- prog list "myFunction1" ---
0000 (s.go:5) TEXT myFunction1+0(SB),$16-24
0001 (s.go:6) MOVQ $type."".MyStructType+0(SB),(SP)
0002 (s.go:6) CALL ,runtime.new+0(SB)
0003 (s.go:6) MOVQ 8(SP),AX
0004 (s.go:8) MOVQ AX,.noname+0(FP)
0005 (s.go:8) MOVQ $0,.noname+8(FP)
0006 (s.go:8) MOVQ $0,.noname+16(FP)
0007 (s.go:8) RET ,
--- prog list "myFunction2" ---
0008 (s.go:11) TEXT myFunction2+0(SB),$0-16
0009 (s.go:12) LEAQ chunk+0(SP),DI
0010 (s.go:12) MOVQ $0,AX
0011 (s.go:14) LEAQ .noname+0(FP),BX
0012 (s.go:14) LEAQ chunk+0(SP),BX
0013 (s.go:14) MOVQ $0,.noname+0(FP)
0014 (s.go:14) MOVQ $0,.noname+8(FP)
0015 (s.go:14) RET ,
ここでのmyFunction1の出力は、peterSOの(優れた)回答とは異なることを心配しないでください。明らかに異なるコンパイラを実行しています。それ以外の場合は、myFunction2を変更して*myStructTypeではなくmyStructTypeを返すようにしました。runtime.newの呼び出しがなくなりました。これは、場合によっては良いことです。ちょっと待ってください、これがmyFunction3です。
--- prog list "myFunction3" ---
0016 (s.go:21) TEXT myFunction3+0(SB),$8000000-8000016
0017 (s.go:22) LEAQ chunk+-8000000(SP),DI
0018 (s.go:22) MOVQ $0,AX
0019 (s.go:22) MOVQ $1000000,CX
0020 (s.go:22) REP ,
0021 (s.go:22) STOSQ ,
0022 (s.go:24) LEAQ chunk+-8000000(SP),SI
0023 (s.go:24) LEAQ .noname+0(FP),DI
0024 (s.go:24) MOVQ $1000000,CX
0025 (s.go:24) REP ,
0026 (s.go:24) MOVSQ ,
0027 (s.go:24) MOVQ $0,.noname+8000000(FP)
0028 (s.go:24) MOVQ $0,.noname+8000008(FP)
0029 (s.go:24) RET ,
それでもruntime.newの呼び出しはなく、実際には8MBのオブジェクトを値で返すように機能します。それは機能しますが、通常はしたくないでしょう。ここでのポインタのポイントは、8MBのオブジェクトをプッシュしないようにすることです。