
C言語基礎|自動記憶域期間と静的記憶域期間
C言語の変数って、「宣言したらずっと生きてる」わけじゃないんです。
ある変数は関数を抜けた瞬間に消えるし、ある変数はプログラム終了までずっと残り続けます。
この“変数の寿命”を表す考え方が 記憶域期間(storage duration)。
ここを理解すると、次のようなモヤモヤが一気に晴れます。
- 関数を呼び出すたびに、変数が毎回0に戻るのはなぜ?
- static を付けると、なぜ前回の値を覚えているの?
- グローバル変数がずっと生きてるのはなぜ?

記憶域期間とは?
記憶域期間=オブジェクト(変数)がいつ作られて、いつ破棄されるか です。
有効範囲(scope)が「名前が通用する範囲」だったのに対して、記憶域期間は「実体が生きてる期間」です。
有効範囲と記憶域期間の違い
| 観点 | 何を決める? | 例 |
|---|---|---|
| 有効範囲(scope) | その名前が使える範囲 | ブロックの中だけ見える変数 |
| 記憶域期間(storage duration) | その変数が存在する寿命 | 関数を抜けたら消える変数 |
自動記憶域期間(automatic storage duration)
関数の中で、普通に宣言した変数はだいたいこれです。
自動記憶域期間の特徴
- 宣言位置に到達したときに生成
- ブロックを抜けると破棄
- 初期化しなければ 不定値(ゴミ値の可能性)
例(イメージ)
void f(void)
{
int a = 0; // ここに来たら生成されて0で初期化
} // ここでaは破棄静的記憶域期間(static storage duration)
次の2つは静的記憶域期間になります。
- 関数の外で宣言された変数(いわゆるグローバル変数)
- 関数の中でも static を付けて宣言した変数
静的記憶域期間の特徴
- main実行前の準備段階で生成
- プログラム終了まで存在
- 初期化しなければ 自動的に0で初期化(配列なら全要素0)
サンプルプログラム
元の ax / sx / fx ではなく、呼び出し回数カウンタの例に変えます。
「毎回リセットされるカウンタ」と「覚えているカウンタ」を並べて比較します。
自動と静的の違いを確認する
プロジェクト名:chap6-21-1 ソースファイル名:chap6-21-1.c
// 自動記憶域期間と静的記憶域期間(呼び出し回数の比較)
#include <stdio.h>
int total_calls = 0; // 静的記憶域期間(ファイル有効範囲)
void report(void)
{
static int saved = 0; // 静的記憶域期間(ブロック有効範囲)
int reset = 0; // 自動記憶域期間(ブロック有効範囲)
printf("reset=%d saved=%d total_calls=%d\n", reset++, saved++, total_calls++);
}
int main(void)
{
puts("reset saved total_calls");
puts("----------------------");
for (int i = 0; i < 5; i++)
report();
puts("----------------------");
return 0;
}実行イメージ
reset saved total_calls
----------------------
reset=0 saved=0 total_calls=0
reset=0 saved=1 total_calls=1
reset=0 saved=2 total_calls=2
reset=0 saved=3 total_calls=3
reset=0 saved=4 total_calls=4
----------------------なぜこうなる?をイメージで理解しよう
変数が「いつ作られ」「いつ消える」か
プログラム開始前
total_calls と saved が生成される(0で初期化)
main開始
i が生成される(ループ用)
report呼び出し(1回目)
reset が生成される(0で初期化)
表示 → resetは1になる
report終了
reset は破棄される
report呼び出し(2回目)
reset がまた生成される(0で初期化に戻る)
saved と total_calls は前回の続き
補足(図のポイント)
- reset は report の中だけに存在する “はかない命”
→ 呼び出すたびに作り直されるので、毎回0から - saved は static 付きなので “ずっと生きる”
→ 1回目は0、2回目は1…と増え続ける - total_calls は関数の外なので “ずっと生きる”
→ saved と同じく増え続ける
自動と静的のまとめ
オブジェクトの記憶域期間
| 項目 | 自動記憶域期間 | 静的記憶域期間 |
|---|---|---|
| 生成 | 宣言に到達したとき | main実行前の準備段階 |
| 破棄 | ブロックを抜けたとき | プログラム終了時 |
| 初期化なし | 不定値になり得る | 0(数値)、0.0(実数)、配列は全要素0 |
登場する命令の書式と役割
今回出てきた要素まとめ
| 命令・要素 | 書式 | 何をする? |
|---|---|---|
| static | static 型 変数名; | 変数に静的記憶域期間を与える(値を保持し続ける) |
| printf | printf(書式, …); | 値を表示する |
| puts | puts(文字列); | 文字列を表示して改行 |
| for | for (初期化; 条件; 更新) 文 | 繰り返し |
| ++ | 変数++ | 変数を1増やす(式の値は増やす前の値) |
auto と register について(今どきの感覚で)
- auto は書いても書かなくても同じ(昔の名残)
- register は「レジスタに置いてね」というお願い
ただし今はコンパイラ最適化が賢いので、実務ではほぼ使いません
例(意味はあるが、今は出番少なめ)
register int k = 0;
静的記憶域期間は「勝手に0初期化」される
静的記憶域期間の変数は、初期化を書かなくても 0 に初期化されます。
(数値型は0、doubleは0.0、配列は全要素0)
演習問題
演習6-14:配列の0初期化確認
静的記憶域期間をもつ int 配列 data の全要素が 0 で初期化されることを確認するプログラムを作成せよ。
要素数は 4 とする。
解答例
プロジェクト名:chap6-21-1 ソースファイル名:chap6-21-1.c
#include <stdio.h>
static int data[4]; // 全要素が0で初期化される
int main(void)
{
for (int i = 0; i < 4; i++)
printf("data[%d] = %d\n", i, data[i]);
return 0;
}解説
data は static 付きなので静的記憶域期間です。初期化子を書かなくても、main開始前に全要素が0になります。
もし同じことを関数内の普通の配列(自動記憶域期間)でやると、初期化しない限り不定値になり得ます。
演習6-15:呼び出し回数の表示
関数 show_count を作成せよ。呼び出されるたびに、1回目、2回目、3回目…と表示する。
関数の形は次のとおり。
void show_count(void);
解答例
プロジェクト名:chap6-21-2 ソースファイル名:chap6-21-2.c
#include <stdio.h>
void show_count(void)
{
static int count = 0;
count++;
printf("show_count : %d回目\n", count);
}
int main(void)
{
show_count();
show_count();
show_count();
return 0;
}
解説
count を static にすることで、show_count を抜けても count の値が保持されます。
もし static を付けないと、毎回countが作り直されて同じ表示になってしまいます。
