変数とメモリ


C言語でのメモリの使い方

C言語でのメモリの使われ方は、次の4つに分類できる。

コード(code)領域

プログラムが格納される領域。コンパイルされて生成された機械語のプログラムを コピーし、CPUはそれを順次読み込んで実行する。「関数へのポインタ」は この領域のどこかを指すはず。

データ(data)領域

グローバル変数が格納される。プログラムの開始時に確保され、プログラム終了まで ずっと存在している。

ヒープ(heap)領域

mallocによって割り当てられるメモリ領域。プログラム開始時には 存在せず、mallocの実行時にOSから「もらって」来る。 freeでOSに「返却」する必要がある。

スタック(stack)領域

2つの目的に使われる。 この領域はLIFO (Last-In First-Out)な構造を持ち、 スタックポインタと呼ばれるCPU内のレジスタで管理されている。

関数の戻るべき場所を記憶するためには、このようなデータ構造に せざるを得ない。関数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終了後の戻り先
 
(使用中)
 

static変数

(あまり使用頻度は高くないが一応説明しておく。よく理解出来なくても 問題ない。)

C言語にはstaticというキーワードがあって、 変数の宣言時に、

static int a;
のようにstaticを付けることが出来る。但し、この意味は 関数内で宣言した場合と関数外で宣言した場合で全く違うので 注意が必要である。

関数内で宣言したstatic変数

普通、関数内で宣言した変数はスタック領域に取られるが、 staticが付いているとデータ領域に取られる。 こうすると、この変数の初期化はグローバル変数と同様プログラム 開始時に一回だけ行われ、プログラム終了までずっと存在している。 従って、関数を抜けても値は失われない。

#include <stdio.h>

void func()
{
    static int c = 0;

    printf("%d\n", c);
    c++;
}

int main(void)
{
    func();
    func();
    func();

    return 0;
}
static-sample.c

このプログラムを実行すると、

0
1
2
と表示される。「その関数に入っているときだけ名前の見えるグローバル変数」 と思うのが分かりやすいかも。

関数外で宣言したstatic変数

こちらはstaticの無い通常の関数外で宣言された変数と同様、 格納されるのはデータ領域のままである。が、その変数が見えるのは 「その宣言が書かれたファイル(.c)」の中だけになる。 一つのファイルだけでプログラムが書かれているならば通常のグローバル 変数と同じだが、複数ファイルを使って分割コンパイルをした場合は、 staticを使うと変数の見える範囲を「そのファイル」に限定する ことが出来るので、情報の隠蔽が可能となる。staticが無ければ 全プログラムから見える。
変数とメモリ