メモリの地図

プログラムが使う3つの置き場所

第1章では、ポインタが「値そのものではなく、値の置き場所を指すもの」だと確かめました。ここからは、その置き場所がどこにあるのかを見ていきます。

実行中のCプログラムは、メモリを用途ごとに分けて使っています。よく使うのは次の3つです。

  • 静的領域 — グローバル変数と static を付けた変数が置かれる。プログラムが始まってから終わるまでずっと同じ場所にある
  • スタック — 関数のローカル変数と引数が置かれる。関数を呼ぶと積まれ、戻ると降ろされる
  • ヒープmalloc で自分から要求したときだけ使える。返すまで生き続ける

この3つの違いは、大きさでも速さでもなく、いつ生まれていつ消えるかという寿命の違いです。ここを取り違えると、すでに消えた場所を読み書きしてしまい、Cで最も追いにくいバグになります。

静的領域はずっとある

グローバル変数は、プログラムが起動した時点で用意され、終了まで残ります。関数の中で static int called = 0; と書いた変数も同じで、置き場所は静的領域です。名前は関数の中でしか使えませんが、値は関数を抜けても残ったままになります。

void f(void) { static int called = 0; /* 静的領域。前回の値が残る */ int local = 0; /* スタック。呼ぶたびに作り直される */ called++; local++; }

static 付きの初期化 = 0 は、呼ばれるたびに実行されるのではなく、プログラム開始時に1度だけ行われます。だから2回目の呼び出しでは0に戻りません。

スタックは呼び出しのあいだだけ

対して local は関数に入るたびに新しく作られ、抜けると無くなります。ですから何回呼んでも local は1のままです。この作り直しがあるおかげで、同じ関数を再帰で何重に呼んでも変数が混ざりません。

ヒープは自分で始まりと終わりを決める

3つ目のヒープは、この章の後半で malloc を覚えてから使います。静的領域は寿命が長すぎ、スタックは短すぎるという場面、たとえば「実行してみるまで何人分要るか分からない」ときに使う置き場所です。

固定長の配列では、宣言の時点で要素数を決めなければなりません。100人分と書けば10人のときに無駄になり、1000人来たら足りません。この「実行するまで決まらない量」を扱えるのがヒープです。

どれがどこに置かれるか

第1章で扱った文字列リテラルも思い出してください。char *s = "田中";"田中" 本体は書き換えできない読み取り専用の領域に置かれ、s というポインタ変数だけがスタックにあります。同じ1行の中でも、置き場所が2つに分かれています。

書き方置き場所寿命
int total; を関数の外に書く静的領域開始から終了まで
関数の中の static int n;静的領域開始から終了まで
関数の中の int n;スタック関数を抜けるまで
malloc で確保した領域ヒープfree するまで

番地ではなく寿命で確かめる

メモリの説明ではよくアドレスを表示して並びを見せますが、実際の番地はOSや実行のたびに変わるので、覚えても意味がありません。見るべきなのは寿命です。今回は、同じ関数を3回呼んで、値が残る変数と毎回0に戻る変数を並べて表示し、置き場所の違いを出力の形で確かめます。

要件

  1. 呼ばれた回数は関数の中の static 変数で数える
  2. 毎回作り直される変数は普通のローカル変数にする
  3. 合計はグローバル変数 g_total に足していく
  4. 出力は called=1 local=1 total=80 の形で1行ずつ

入出力例

main("") → "called=1 local=1 total=80 called=2 local=1 total=170 called=3 local=1 total=240"

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.c
学習モード

メモ

メモリの地図

⌘S で保存