[Go言語] スコープを抜けた変数の扱われ方(C言語との対比)

Go言語は、一見C言語のようでいて、かなり手厚い機能も備えている。

一つの例に、スコープを抜けた変数の生存期間がある。

ガベージコレクションありきの環境に慣れ親しんだ人にとっては当たり前の挙動かもしれないが、

Go言語は変数に参照がある限り値を参照し続けられるように、

スコープを抜けた後も参照されている変数はスタック領域からヒープ領域に移される。

通常、この処理を開発者(Go言語を使って開発する人)が意識する必要はなく、

ヒープ領域にあるからといってメモリ領域の開放などを開発者が意識しなければいけないなんてこともない。

例えば次のコードについて考える。

main 関数は、ゴルーチン TestRoutine() を生成・実行する。(ゴルーチンはC言語のスレッドに相当するが、処理単位やタイミングを制御するコンテキストが異なるため同一概念ではない)

TestRoutine() 内では変数 testVar が宣言され、そのアドレスがチャネルを介して main 関数に返される。

main 関数にアドレス値が渡ってきた時点で TestRoutine() 関数の実行は終了しているため、

C言語の感覚で考えればアドレスが指す先の値がどうなるかの保証は無いように思われるが、

実際は main 関数内でも 123 という値が読み取れる。

実際に上記のコードをコンソールで実行すると、次の出力が得られる。

TestRoutine - testVar:123
main - testVar:123

一方、これと似たようなことをC言語で素直にやろうとすると、例えば次のようなコードになる。

main 関数は pthread_create 関数により新たにスレッドを生成し、そのスレッド上で TestRoutine 関数を実行する。

TestRoutine 関数内で int 型変数 testVar を宣言し、testVar のアドレスを main 関数に返す。

このコードはビルドが通るが、実行時にスコープを抜けた testVar が破棄されるため、

16行目で testVar の値を参照しようとすると例外が発生しクラッシュしてしまう。

Go言語のように、スコープを抜けた後も値を参照できるようにするためには、

testVarの値をヒープ領域にコピーし、然るべきタイミングでそのメモリ領域を開放する処理を開発者自身の手で作り込んでおく必要がある。

コメントする