404 Not Found

404 Not Found


nginx

適用範囲と有効期間

変数を作成したとき、どこでそれを使うことができるのでしょうか?また、その変数はどのくらいの期間存在し続けるのでしょうか?これらはスコープ存続期間に関する質問です。これらは密接に関連しているものの、同じ概念ではありません。


1. スコープ(変数が可視となる範囲)

スコープは、コード内のどこで変数にアクセスできるかを決定します。

(1) 1.1 ローカルスコープ

関数やブロック { } 内で宣言された変数は、そのブロック内でのみ参照可能です。

CPP
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 ネストされたブロック

ブロックはネストすることができ、内側のブロックは外側のブロックの変数にアクセスできます。

CPP
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. ブロックのスコープ

forwhile、またはif文で宣言された変数は、ブロックスコープを持ちます:

CPP
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. グローバル変数

関数の外で宣言された変数はグローバルであり、どこからでもアクセスできます。

CPP
#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) グローバル変数に関する問題

  1. 名前の競合 — 複数のファイルが同じグローバル名を使用している場合
  2. デバッグが難しい — どの関数でもグローバル変数を変更できるため、バグの追跡が困難になる
  3. スレッドセーフ — マルチスレッドコードでは、グローバル変数に対して同期処理が必要となる
⚠️ 注意: どうしても必要な場合を除き、グローバル変数の使用は避けてください。グローバル変数を使用すると、コードの理解やメンテナンスが難しくなります。代わりに、引数や戻り値を使用してください。



5. 静的変数

static ローカル変数は一度初期化されると、関数の呼び出しの間もその値が保持されます。

CPP
#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) ▶ サンプル:静的解析による関数呼び出しの追跡(難易度 ⭐⭐)

CPP
#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 スコープ外の変数の使用

CPP
int main() {
 if (true) {
 int x = 100;
 }
 // std::cout << x; // ❌ x doesn't exist here
 return 0;
}

(2) 7.2 スコープと存続期間の混同

変数はスコープ内にあるものの、その存続期間が終了する場合があります(例:ローカル変数への参照を返す場合など):

CPP
int& badFunction() {
 int x = 5;
 return x; // ❌ x's lifetime ends here!
 // The caller gets a dangling reference
}
🔥 よくある間違い: これは ダングリング参照 と呼ばれます。つまり、参照先が解放済みのメモリを指している状態です。これを使用すると、未定義の挙動(クラッシュやゴミデータ)が発生します。


▶ サンプル 1: scope and lifetimeデモ (難易度 ⭐)

CPP
#include <iostream>

int main() {
 // scope and lifetimeの
 std::cout << "scope and lifetimeをびましょう!" << std::endl;
 return 0;
}
▶ 試してみよう

❓ よくある質問

Q スコープとライフタイムの違いは何ですか?
A スコープとは、変数が「どこで」可視であるか(どのコードブロックからアクセスできるか)を指します。ライフタイムとは、変数が「どのくらいの期間」存在するか(いつ生成され、いつ破棄されるか)を指します。ローカル変数はブロックスコープを持ち、ブロックのライフタイムを持ちます。スタティックローカル変数はブロックスコープを持ちますが、プログラムのライフタイムを持ちます。
Q 静的変数はいつ使うべきですか?
A カウンター、キャッシュ、単一インスタンスフラグなど、関数が呼び出しの合間に状態を保持する必要がある場合に使用します。静的変数は、1つの関数しか変更できないため、グローバル変数よりも安全です。
Q なぜグローバル変数は避けるべきですか?
A グローバル変数は隠れた依存関係を生み出します。どの関数でもグローバル変数を変更できてしまうため、コードが予測不能になり、デバッグが困難になります。状態を共有するには、パラメータを渡すか、クラスを使用してください。
Q クラス内に静的変数を定義することはできますか?
A はい。静的クラスメンバーは、すべてのインスタンスで共有されます。これについては、OOPのセクションで詳しく説明します。

📖 まとめ


📝 練習問題

  1. 初心者(難易度 ⭐): 静的カウンタを使用して、その関数が何回呼び出されたかを追跡する関数を作成してください。ループ内でその関数を10回呼び出し、出力を確認してください。

  2. 中級(難易度 ⭐⭐): グローバル変数を共有する2つの関数を作成してください。1つは変数を1増やし、もう1つは1減らします。これらを交互に呼び出し、呼び出しのたびにその値を出力してください。

  3. 上級(難易度 ⭐⭐⭐): 「ぶら下がり参照」の問題を実証してみましょう。ローカル変数への参照を返す関数を作成します。それを呼び出して、未定義の挙動を確認してください。その後、値渡しに変更して修正します。



8. 🚀 次は

次に、関数について学んだこと(第15課)を、完全なプロジェクトを通じてすべて実践していきます。そのプロジェクトとは、数学用ユーティリティライブラリ、数字当てゲーム、そして学生の成績管理システムです。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%