2009/11/14
プログラミング言語Goで謎だった部分
ソースを取り直したらGoが動きました。最初に動かなかった理由は不明です。実際に動かしてみると、不明だった部分の謎が解けました。
- 謎だった部分
- スタックに積んだように見えるローカルオブジェクトのアドレスを関数の外に返せる理由
結論から言うと、ローカルオブジェクトに対するアドレス参照があると、そのオブジェクトをヒープに確保するように動作が変わります。
- 注意
- 以下、敢えてすべての変数に型を明示しています。Go的には := を使って、自明な変数の型を省略するのが流儀です。この文書はCあるいはJavaプログラマのために敢えて型を明示しています。Goでプログラムを書くときは真似しないでください。言語の流儀には従うべきだからです。
前段階として、まずは本当にローカルオブジェクトをスタック上に確保しているかの確認です。以下のような感じです。ちなみに使わない変数があるとコンパイルエラーになるので意味のない代入処理をしています。
// x.goの一部 func myFunc() { var a [256]byte; a[0] = 'a'; }
// 8g -S x.goの出力の一部 0000 (x.go:7) TEXT myFunc+0(SB),$264-0 0001 (x.go:6) MOVL $0,AX 0002 (x.go:6) LEAL a+-256(SP),DI 0003 (x.go:6) MOVL $64,CX 0004 (x.go:6) REP , 0005 (x.go:6) STOSL , 0006 (x.go:7) LEAL a+-256(SP),BX 0007 (x.go:7) MOVL BX,.noname+-264(SP) 0008 (x.go:7) MOVL .noname+-264(SP),BX 0009 (x.go:7) ADDL $0,BX 0010 (x.go:7) MOVL BX,.noname+-260(SP) 0011 (x.go:7) MOVL .noname+-260(SP),BX 0012 (x.go:7) MOVB $97,(BX) 0013 (x.go:7) RET ,
細かい部分の見方はさておき、myFuncの行の $264 が、関数呼び出し時のスタックサイズの拡張バイト数だと思ってください。ローカルオブジェクトの配列への要素アクセスをSP(スタックポインタ)から相対アドレスで行っている雰囲気は分かると思います。
次のように、このローカルオブジェクトのアドレス値をポインタで返してみます。C言語であればバグです。
// x.goの一部 func myFunc() *[256]byte { var a [256]byte; a[0] = 'a'; return &a; }
これは次のコードを吐き出します。
// 8g -S x.goの出力の一部 0000 (x.go:8) TEXT myFunc+0(SB),$24-4 0001 (x.go:8) MOVL $256,(SP) 0002 (x.go:6) CALL ,runtime.mal+0(SB) 0003 (x.go:6) MOVL 4(SP),AX 0004 (x.go:7) MOVL AX,.noname+-16(SP) 0005 (x.go:7) MOVL .noname+-16(SP),BX 0006 (x.go:7) ADDL $0,BX 0007 (x.go:7) MOVL BX,.noname+-12(SP) 0008 (x.go:7) MOVL .noname+-12(SP),BX 0009 (x.go:7) MOVB $97,(BX) 0010 (x.go:8) MOVL AX,.noname+0(FP) 0011 (x.go:8) RET ,
細かい部分はさておき、3行目のCALLがmalloc相当の処理を呼んでいると思ってください。動きが変わりました。凄いと思うか気持ち悪いと思うかは、見る人の文化によって異なると思います。
アドレス値を返さなくても、単にローカルオブジェクトにアドレス参照演算(&)を適用しただけでローカルオブジェクトがヒープに確保されます。ただし、これはコンパイラの最適化で改善されるかもしれません。
入れ子の関数が外側のローカルオブジェクトにアクセスする場合も、ローカルオブジェクトがヒープに確保されます(面倒なので対応するアセンブリのコードは載せません)。
// 256バイトの配列はヒープに確保されます func myFunc() { var a [256]byte; a[0] = 'a'; var f func(); f = func() { fmt.Printf("closure %c", a[0]); }; f(); }
- Category(s)
- カテゴリなし
- The URL to Trackback this entry is:
- http://dev.ariel-networks.com/Members/inoue/go-allocation/tbping