適用範囲と有効期間
変数を作成したとき、どこでそれを使うことができるのでしょうか?また、その変数はどのくらいの期間存在し続けるのでしょうか?これらはスコープと存続期間に関する質問です。これらは密接に関連しているものの、同じ概念ではありません。
1. スコープ(変数が可視となる範囲)
スコープは、コード内のどこで変数にアクセスできるかを決定します。
(1) 1.1 ローカルスコープ
関数やブロック { } 内で宣言された変数は、そのブロック内でのみ参照可能です。
void myFunction() {
int x = 10; // Local to myFunction
// x is accessible here
}
int main() {
// x is NOT accessible here — it's out of scope
// std::cout << x; // ❌ Error!
return 0;
}
(2) 1.2 ネストされたブロック
ブロックはネストすることができ、内側のブロックは外側のブロックの変数にアクセスできます。
int main() {
int a = 5; // Outer block
{
int b = 10; // Inner block
std::cout << a << b; // ✅ Can access both
}
// std::cout << b; // ❌ b is out of scope
return 0;
}
2. ブロックのスコープ
for、while、またはif文で宣言された変数は、ブロックスコープを持ちます:
for (int i = 0; i < 10; i++) { // i is scoped to this loop
std::cout << i;
}
// std::cout << i; // ❌ i is out of scope!
if (int result = someFunction(); result > 0) { // C++17: result scoped to if
std::cout << result;
}
// std::cout << result; // ❌ out of scope
3. ライフタイム(変数が存在する期間)
ライフタイムは、変数がいつ生成され、いつ破棄されるかを決定します。
| 変数型 | 生成 | 破棄 | 存続期間 |
|---|---|---|---|
| ローカル変数 | 宣言箇所に実行が到達したとき | ブロックが終了したとき | ブロックの実行期間 |
| 静的ローカル変数 | 初めてその箇所に到達したとき | プログラムが終了したとき | プログラム全体の実行中 |
| グローバル変数 | main() の開始前 |
プログラム終了時 | プログラム全体の実行中 |
| 動的変数(新規) | new を呼び出したとき |
delete を呼び出したとき |
ユーザーが制御する |
4. グローバル変数
関数の外で宣言された変数はグローバルであり、どこからでもアクセスできます。
#include iostream
int globalCounter = 0; // Global variable
void increment() {
globalCounter++; // Can access global
std::cout << "Counter: " << globalCounter << std::endl;
}
int main() {
increment(); // Counter: 1
increment(); // Counter: 2
globalCounter = 100; // Can also modify from main
increment(); // Counter: 101
return 0;
}
(1) グローバル変数に関する問題
- 名前の競合 — 複数のファイルが同じグローバル名を使用している場合
- デバッグが難しい — どの関数でもグローバル変数を変更できるため、バグの追跡が困難になる
- スレッドセーフ — マルチスレッドコードでは、グローバル変数に対して同期処理が必要となる
5. 静的変数
static ローカル変数は一度初期化されると、関数の呼び出しの間もその値が保持されます。
#include iostream
void counter() {
static int count = 0; // Initiaized only once!
count++;
std::cout << "Called " << count << " times" << std::endl;
}
int main() {
counter(); // Called 1 times
counter(); // Called 2 times
counter(); // Called 3 times
return 0;
}
(1) 静的変数とグローバル変数
| 静的 ローカル | グローバル |
|---|---|
| 関数内でのみ可視 | どこでも可視 |
| 最初の呼び出し時に初期化される | main の実行前に初期化される |
| カプセル化されている(良い) | 露出している(悪い) |
6. 演習:カウンター関数
(1) ▶ サンプル:静的解析による関数呼び出しの追跡(難易度 ⭐⭐)
#include iostream
#include string
void showMessage(const std::string& msg) {
static int callCount = 0;
callCount++;
std::cout << "[" << callCount << "] " << msg << std::endl;
}
int main() {
showMessage("Starting program");
showMessage("Processing data");
showMessage("Saving results");
showMessage("Done");
return 0;
}
7. よくある間違い
(1) 7.1 スコープ外の変数の使用
int main() {
if (true) {
int x = 100;
}
// std::cout << x; // ❌ x doesn't exist here
return 0;
}
(2) 7.2 スコープと存続期間の混同
変数はスコープ内にあるものの、その存続期間が終了する場合があります(例:ローカル変数への参照を返す場合など):
int& badFunction() {
int x = 5;
return x; // ❌ x's lifetime ends here!
// The caller gets a dangling reference
}
▶ サンプル 1: scope and lifetimeデモ (難易度 ⭐)
#include <iostream>
int main() {
// scope and lifetimeの
std::cout << "scope and lifetimeをびましょう!" << std::endl;
return 0;
}
❓ よくある質問
📖 まとめ
- スコープは、変数がどこで可視であるかを決定します(ブロック、関数、ファイル、グローバル)。
- 有効期間 は、変数がどのくらいの期間存在するか(ブロック、プログラム、または手動)を決定します。
- グローバル変数はどこからでも参照できますが、使用は控えめにすべきです
- 静的ローカル変数は、関数の呼び出し間でも保持されますが、その関数内でのみ参照可能です。
- ローカル変数への参照を絶対に返さないでください。ダングリング参照が発生します。
📝 練習問題
-
初心者(難易度 ⭐): 静的カウンタを使用して、その関数が何回呼び出されたかを追跡する関数を作成してください。ループ内でその関数を10回呼び出し、出力を確認してください。
-
中級(難易度 ⭐⭐): グローバル変数を共有する2つの関数を作成してください。1つは変数を1増やし、もう1つは1減らします。これらを交互に呼び出し、呼び出しのたびにその値を出力してください。
-
上級(難易度 ⭐⭐⭐): 「ぶら下がり参照」の問題を実証してみましょう。ローカル変数への参照を返す関数を作成します。それを呼び出して、未定義の挙動を確認してください。その後、値渡しに変更して修正します。
8. 🚀 次は
次に、関数について学んだこと(第15課)を、完全なプロジェクトを通じてすべて実践していきます。そのプロジェクトとは、数学用ユーティリティライブラリ、数字当てゲーム、そして学生の成績管理システムです。



