C++: スコープと寿命
最終更新:2026-08-31
変数はどこで宣言するかでスコープ(どこで見えるか)と寿命(いつ存在するか)が決まる。これを理解しないと、予測不能なバグに遭遇する。
1. スコープとは何か?
(1) 1.1 定義
スコープは、変数がアクセス可能なコードの領域。
(2) 1.2 スコープの種類
| 種類 | 説明 | 例 |
|---|---|---|
| ローカルスコープ | ブロック内で宣言、ブロック内でのみ可視 | 関数内の変数 |
| グローバルスコープ | すべての関数の外で宣言、どこでも可視 | グローバル変数 |
| ブロックスコープ | {}内で宣言、ブロック内でのみ可視 |
ループ内の変数 |
| 名前空間スコープ | 名前空間内で宣言 | std::cout |
2. ローカル変数
(1) 2.1 特徴
- 関数またはブロック内で宣言
- ブロックに入ると作成、ブロックを出ると破棄
- 初期化されていないとゴミ値が入る
(2) 2.2 例
CPP
#include <iostream>
void func() {
int x = 10; // ローカル変数
std::cout << x << std::endl;
} // xはここで破棄
int main() {
func();
// std::cout << x; // ❌ エラー: xはここでは見えない
return 0;
}
3. グローバル変数
(1) 3.1 特徴
- すべての関数の外で宣言
- プログラム開始時に作成、終了時に破棄
- デフォルトで0に初期化
(2) 3.2 例
CPP
#include <iostream>
int counter = 0; // グローバル変数
void increment() {
counter++;
}
int main() {
increment();
increment();
std::cout << counter << std::endl; // 2
return 0;
}
⚠️ 注意: グローバル変数は避ける。どの関数でも変更でき、バグの原因になる。どうしても必要な場合のみ使う。
4. スタティック変数
(1) 4.1 ローカルスタティック変数
関数内でstaticとして宣言。プログラム終了まで存在。
CPP
#include <iostream>
void countCalls() {
static int calls = 0; // 初回だけ初期化
calls++;
std::cout << "呼び出し回数: " << calls << std::endl;
}
int main() {
countCalls(); // 1
countCalls(); // 2
countCalls(); // 3
return 0;
}
(2) 4.2 グローバルスタティック変数
ファイル内でのみ可視なグローバル変数。
5. ブロックスコープ
{}で作られるスコープ:
CPP
#include <iostream>
int main() {
int x = 10;
{
int y = 20;
std::cout << x << " " << y << std::endl; // OK: 両方可視
}
// std::cout << y; // ❌ エラー: yはブロック外
return 0;
}
6. 名前の隠蔽
内側のスコープで同じ名前の変数を宣言すると、外側の変数を隠す:
CPP
#include <iostream>
int x = 100; // グローバル
int main() {
int x = 10; // ローカルがグローバルを隠す
std::cout << x << std::endl; // 10
std::cout << ::x << std::endl; // 100 (::でグローバルにアクセス)
return 0;
}
▶ サンプル 1:スタティック変数で呼び出し回数を追跡(難易度 ⭐)
CPP
#include <iostream>
void login() {
static int loginCount = 0;
loginCount++;
std::cout << "ログイン回数: " << loginCount << std::endl;
}
int main() {
login();
login();
login();
return 0;
}
出力:
TEXT 📖 参照専用ログイン回数: 1 ログイン回数: 2 ログイン回数: 3
▶ サンプル 2:ブロックスコープ(難易度 ⭐⭐)
CPP
#include <iostream>
int main() {
int x = 5;
std::cout << "外側のx: " << x << std::endl;
{
int x = 10; // 外側のxを隠す
std::cout << "内側のx: " << x << std::endl;
}
std::cout << "また外側のx: " << x << std::endl;
return 0;
}
▶ サンプル 3:グローバル変数の危険性(難易度 ⭐⭐)
CPP
#include <iostream>
int total = 0; // グローバル変数
void add(int value) {
total += value; // グローバル変数を変更
}
void reset() {
total = 0; // 意図しないリセットの可能性
}
int main() {
add(10);
add(20);
std::cout << "合計: " << total << std::endl; // 30
reset(); // 誤ってリセット
add(5);
std::cout << "リセット後: " << total << std::endl; // 5
return 0;
}
⚠️ 注意: この例はグローバル変数の危険性を示している。どの関数でも変更できるため、バグの追跡が困難になる。
❓ よくある質問
Q 変数を初期化しないとどうなる?
A ローカル変数はゴミ値が入る(予測不能)。グローバル/スタティック変数は0に初期化。常に明示的に初期化しよう。
Q グローバル変数はいつ使う?
A 避ける。どうしても必要な場合(設定値、ログ、状態)にのみ使い、必要最小限に。
Q スタティック変数はどこに保存される?
A ローカルスタティック変数はグローバル変数と同様、静的メモリ(.data/.bssセグメント)に保存。関数終了後も残る。
📖 まとめ
- スコープは変数が可視な領域
- 寿命は変数が存在する期間
- ローカル変数はブロック内でのみ存在
- グローバル変数はプログラム全体で存在——避ける
- スタティック変数は関数呼び出し間で値を保持
- 内側のスコープは外側の同名変数を隠す
📝 練習問題
-
初級(難易度 ⭐): 関数を作成し、スタティック変数を使って何回呼び出されたかを出力。
-
中級(難易度 ⭐⭐): ローカル変数とグローバル変数の違いを示すプログラムを書く。
-
上級(難易度 ⭐⭐⭐): ブロックスコープを悪用して読みにくいコードを書く。次に、同じ機能をスコープを整理して書き直す。