C言語でプログラミングをしていると、「この変数は、どこからどこまで使えるんだろう?」「関数を呼び出すたびに、変数の値がリセットされてしまうのはなぜ?」といった疑問にぶつかることがあります。これらの疑問を解決する鍵が、**変数の「スコープ」と「記憶域期間(寿命)」**の理解です。
今回は、変数がプログラムのどこで有効になるのか、そしていつまで値を保持し続けるのか、というC言語の非常に重要な概念を、static
やextern
といったキーワードと共に徹底解説します。
変数が使える範囲「スコープ」とは?
スコープとは、一言でいうと**「その変数や関数が参照(利用)できる範囲」**を定めたルールのことです。変数は宣言された場所によって、どこからアクセスできるかが決まっています。
ブロックスコープ(ローカル変数)
最も基本的なスコープが「ブロックスコープ」です。これは、{}
(波括弧) で囲まれたブロックの中で宣言された変数は、そのブロック内でしか使えない、というルールです。関数の中で宣言される変数(ローカル変数)がこれにあたります。
void myFunction() {
int local_val = 10; // local_valはmyFunctionの中だけで有効
// ここでは local_val を使える
}
int main(void) {
// ここから local_val にアクセスしようとするとコンパイルエラーになる
return 0;
}
この性質により、他の関数で同じ名前の変数を使っていても、互いに影響を与えず、安全にプログラミングができます。
ファイルスコープ(グローバル変数)
一方、どの関数の内側でもなく、ファイルのトップレベルで宣言された変数は「ファイルスコープ」を持ちます。これをグローバル変数と呼びます。
グローバル変数は、宣言されたファイル全体、どの関数からでもアクセスできるという特徴があります。
#include <stdio.h>
// g_app_status はグローバル変数
int g_app_status = 100;
void checkStatus(void);
int main(void) {
printf("main関数から参照: %d\n", g_app_status);
checkStatus();
return 0;
}
void checkStatus(void) {
printf("checkStatus関数から参照: %d\n", g_app_status);
}
実行結果:
main関数から参照: 100
checkStatus関数から参照: 100
このように、main
関数とcheckStatus
関数の両方から同じ変数 g_app_status
が参照できています。
変数が存在する期間「記憶域期間(寿命)」とは?
記憶域期間とは、「変数がメモリ上にいつ生成され、いつまで存在し続けるか」という、変数の寿命に関するルールです。
自動記憶域期間(普通のローカル変数)
関数の中で普通に宣言されたローカル変数は、「自動記憶域期間」を持ちます。これは、その変数が宣言されたブロック {}
が実行される時にメモリが確保され、ブロックを抜ける瞬間に自動で破棄されるという性質です。
そのため、関数が呼び出されるたびに変数は初期化されます。
サンプルコード:
#include <stdio.h>
void countUp(void);
int main(void) {
countUp();
countUp(); // もう一度呼び出す
return 0;
}
void countUp(void) {
int number = 0; // 自動記憶域期間を持つ(呼び出されるたびに0になる)
number += 5;
printf("変数numberの値: %d\n", number);
}
実行結果:
変数numberの値: 5
変数numberの値: 5
2回呼び出しても、結果は同じ5
です。countUp
関数が終了するたびに変数number
が破棄され、次に呼び出されたときに再び0
で作り直されるためです。
静的記憶域期間(グローバル変数や静的変数)
一方、プログラムの開始から終了まで、ずっとメモリ上に存在し続ける変数を「静的記憶域期間」を持つ変数といいます。グローバル変数がこの性質を持ちます。
そして、static
というキーワードを使うことで、ローカル変数に静的記憶域期間を持たせることも可能です。
2つの意味を持つstatic
キーワード
static
キーワードは、付ける場所によって少し意味が変わる、少し特別なキーワードです。
1. 値を保持し続ける「静的ローカル変数」
関数内のローカル変数の宣言にstatic
を付けると、その変数は静的ローカル変数になります。
- スコープ: 通常のローカル変数と同様、宣言された関数内のみ。
- 寿命: プログラムの実行終了まで。関数を抜けても破棄されず、値を保持し続けます。
先ほどのcountUp
関数のnumber
にstatic
を付けてみましょう。
サンプルコード:
#include <stdio.h>
void staticCountUp(void);
int main(void) {
staticCountUp();
staticCountUp(); // もう一度呼び出す
return 0;
}
void staticCountUp(void) {
// 静的ローカル変数(初回のみ0で初期化され、以降は値を保持する)
static int static_number = 0;
static_number += 5;
printf("静的変数static_numberの値: %d\n", static_number);
}
実行結果:
静的変数static_numberの値: 5
静的変数static_numberの値: 10
今度は、2回目の呼び出しで値が10
になりました。static_number
は staticCountUp
関数を抜けても値を保持しているため、前回の5
にさらに5
が加算されたのです。
2. 公開範囲をファイル内に限定するstatic
グローバル変数の宣言にstatic
を付けると、その変数のスコープ(正確にはリンケージ)を、そのファイル内に限定することができます。
これにより、他のCファイルからそのグローバル変数を参照できなくなり、意図しない場所からのアクセスを防いだり、他のファイルで定義された同名のグローバル変数との名前の衝突を避けたりすることができます。大規模なプログラム開発において、非常に重要な機能です。
// このファイルの中だけで使えるグローバル変数
static int file_local_counter = 50;
複数のファイルで変数を共有するextern
extern
は、「別のファイルで定義されているグローバル変数を、このファイルでも使いますよ」とコンパイラに伝えるためのキーワードです。
例えば、main.c
とsub.c
という2つのファイルがある場合、
sub.c
// 実体を定義
int g_shared_value = 200;
main.c
#include <stdio.h>
// sub.cで定義されているg_shared_valueを参照することを宣言
extern int g_shared_value;
int main(void) {
printf("別のファイルで定義された変数の値: %d\n", g_shared_value);
return 0;
}
このように、extern
を使うことで、プロジェクト内の複数のファイルにまたがって変数を共有できます。
ただし、どこからでもアクセスできるグローバル変数の多用は、プログラムの構造を複雑にし、バグの温床になりやすいため、利用は慎重に行うべきです。
まとめ
今回は、C言語における変数のスコープ(有効範囲)と記憶域期間(寿命)について学びました。
種類 | スコープ(範囲) | 寿命(記憶域期間) | 特徴 |
ローカル変数 | ブロック内部 | ブロック実行中(自動) | 最も基本的。関数内で完結する。 |
静的ローカル変数 | ブロック内部 | プログラム終了まで(静的) | 関数を抜けても値を保持する。 |
グローバル変数 | ファイル全体 | プログラム終了まで(静的) | どこからでもアクセス可能。多用は注意。 |
静的グローバル変数 | ファイル内部のみ | プログラム終了まで(静的) | 他のファイルから隠蔽される。 |
これらの違いを正しく理解し、適切な場所で適切な種類の変数を使い分けることが、バグが少なく、メンテナンス性の高いコードを書くための第一歩です。