関数の戻るべき場所を記憶するためには、このようなデータ構造に せざるを得ない。関数A→関数B→関数C のような呼び出しが行われた 場合を考えると、戻りアドレスを記憶させる箇所が1つではうまくいかない ことが分かる。
ローカル変数をこの領域に確保するのは、再帰呼び出し(関数Aから 関数Aを呼び出す)を実現するためである。再帰呼び出しがきちんと 動作するためには、関数Aから関数Aを呼び出した場合、最初の関数Aと 次の関数Aのそれぞれで使うローカル変数が「別物」になっていなければ ならない。
この領域は固定サイズで、一般的にあまり大きくない。システムの種類や設定によるが、 現在の一般的なUNIXではulimit -sで調べることができ、 例えば8192と表示されたら、スタックサイズは8192Kbyte = 8Mbyte。 よって、巨大な配列など、大きなデータを扱う ときはローカル変数とはせず、グローバル変数とするか、mallocでの 確保を考えるべきである。
まとめると、以下の図のような感じ。
コード領域 | プログラム開始時に確保 (サイズ固定) |
データ領域 | |
スタック領域(空き) |
|
--------以下使用中-------- | ← スタックポインタ |
スタック領域(使用中) |
|
|
|
ヒープ領域 | 実行時に確保 (サイズ可変) |
以下は、関数Aから関数Bを呼び出したときのスタック領域の様子:
(空き) |
|
--------以下使用中-------- | ← スタックポインタ |
関数Bのローカル変数 | |
関数B終了後の戻り先 | |
関数Aのローカル変数 | |
関数A終了後の戻り先 | |
(使用中) |
関数Bが終了し関数Aに戻ると、関数Bが使用していた領域は破棄され、 スタックポインタが戻される。
(空き) |
|
--------以下使用中-------- | ← スタックポインタ |
関数Aのローカル変数 | |
関数A終了後の戻り先 | |
(使用中) |
C言語にはstaticというキーワードがあって、 変数の宣言時に、
static int a;のようにstaticを付けることが出来る。但し、この意味は 関数内で宣言した場合と関数外で宣言した場合で全く違うので 注意が必要である。
#include <stdio.h> void func() { static int c = 0; printf("%d\n", c); c++; } int main(void) { func(); func(); func(); return 0; } |
このプログラムを実行すると、
0 1 2と表示される。「その関数に入っているときだけ名前の見えるグローバル変数」 と思うのが分かりやすいかも。