Personal tools
You are here: Home ブログ 井上 プログラミング言語Goで謎だった部分
« December 2010 »
Su Mo Tu We Th Fr Sa
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
Recent entries
Apache2.4のリリース予定は来年(2011年)初め(あくまで予定) inoue 2010-12-23
Herokuの発音 inoue 2010-12-20
雑誌記事「ソフトウェア・テストPRESS Vol.9」の原稿公開 inoue 2010-12-18
IPA未踏のニュース inoue 2010-12-15
労基法とチキンゲーム inoue 2010-12-06
フロントエンドエンジニア inoue 2010-12-03
ASCII.technologies誌にMapReduceの記事を書きました inoue 2010-11-25
技術評論社パーフェクトシリーズ絶賛発売中 inoue 2010-11-24
雑誌連載「Emacsのトラノマキ」の原稿(part8)公開 inoue 2010-11-22
RESTの当惑 inoue 2010-11-22
「プログラマのためのUXチートシート」を作りました inoue 2010-11-19
「ビューティフルコード」を読みました inoue 2010-11-16
Categories
カテゴリなし
 
Document Actions

プログラミング言語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();
}
The URL to Trackback this entry is:
http://dev.ariel-networks.com/Members/inoue/go-allocation/tbping
Add comment

You can add a comment by filling out the form below. Plain text formatting.

(Required)
(Required)
(Required)
This helps us prevent automated spamming.
Captcha Image


Copyright(C) 2001 - 2006 Ariel Networks, Inc. All rights reserved.