10 変数のスコープ

「同じ名前の変数」が複数の場所にある——どれが使われる?

関数を学んだことで、プログラムには「関数の外」と「関数の中」という2つの領域があることがわかりました。 変数にはどこから参照・変更できるかという「有効範囲(スコープ)」があります。

スコープを理解していないと、「変数を変えたつもりなのに変わっていない」「意図しない値が変わった」 という原因不明のバグに悩まされます。 プログラムが大きくなるほど、スコープの理解は不可欠です。

1. 局所変数と大域変数

種類宣言場所有効範囲別名
局所変数関数の中その関数の中だけローカル変数
大域変数関数の外(プログラムの先頭)プログラム全体グローバル変数
プログラム全体 大域変数:整数型: x ← 10 プログラム全体のどこからでも参照・変更できる ○ funcA() 局所変数:整数型: x ← 99 x を出力 → 99(局所変数が優先) ※ 大域変数の x には影響しない ○ funcB() x を出力 → 10(大域変数を参照) x ← 20 大域変数 x が 20 に変わる

2. 局所変数の優先(シャドーイング)

関数の中で大域変数と同じ名前の局所変数を宣言すると、その関数の中では局所変数が優先されます。 大域変数は隠れた状態になり、関数が終了すると局所変数は破棄されます。

文字列型: a ← "Global"   // 大域変数

○ funcA()
    文字列型: a ← "Local"  // 局所変数(同名)
    a を出力               // "Local" が出力される(局所優先)
                           // 大域変数 a は変わらない

○ main()
    a を出力               // "Global"(大域変数)
    funcA()                // "Local" が出力される
    a を出力               // "Global"(大域変数は変わっていない)
呼び出し順 出力内容 大域変数 a の値 ① main → a を出力 "Global" "Global"(変化なし) ② funcA → a を出力 "Local" "Global"(変化なし)

3. 大域変数を関数から変更する

関数内で局所変数として宣言せずに代入すると、大域変数を直接変更できます。

文字列型: a ← "A"   // 大域変数

○ funcC()
    a を出力          // "A"(大域変数を参照)
    a ← "C"          // 大域変数 a を直接変更
    a を出力          // "C"

○ main()
    funcC()
    a を出力          // "C"(大域変数が変更されている)
大域変数の変更は慎重に。
大域変数をどこからでも変更できると、どこで値が変わったのかを追うのが難しくなります。 大規模なプログラムでは、意図しない変更によるバグの温床になります。 基本的には関数は引数で値を受け取り、return で返す設計が推奨されます。

4. まとめ:3つのパターン

パターンコード例結果
関数内で局所変数を宣言 整数型: x ← 99(関数内) 大域変数 x は変わらない。関数内では局所変数が使われる
関数内で大域変数を参照のみ x を出力(宣言なし) 大域変数の値が出力される。変更はしない
関数内で大域変数を変更 x ← 99(宣言なし) 大域変数 x が直接書き換わる

5. 理解度チェック問題

問題10-1

次のプログラムで main() を呼び出したとき、出力される順番として正しいものを選びなさい。

文字列型: ch ← "X"   // 大域変数

○ programA()
    文字列型: ch ← "A"  // 局所変数
    ch を出力

○ programB()
    ch ← "B"            // 大域変数を変更
    ch を出力

○ main()
    ch を出力
    programA()
    ch を出力
    programB()
    ch を出力
解説を表示 正解:ア
① main → 大域変数 ch 出力:"X"
② programA → 局所変数 ch="A" を出力:"A"。大域変数は変わらない
③ main → 大域変数 ch 出力:"X"(変わっていない)
④ programB → 大域変数 ch ← "B" に変更して出力:"B"
⑤ main → 大域変数 ch 出力:"B"(変更されている)
出力:X → A → X → B → B

問題10-2

次のプログラムで programB("X") を呼び出した後に大域変数 a を出力するとどうなるか。

文字列型: a ← "A"   // 大域変数

○ programB(文字列型: b)
    b を出力
    文字列型: a ← b   // 局所変数として宣言
    a を出力
解説を表示 正解:エ
関数内の 文字列型: a ← b は局所変数として宣言しているため、大域変数の a には影響しません。 関数終了後に大域変数 a を出力すると "A" のままです。

問題10-3

次のプログラムで programC() を呼び出したときの出力の順番として正しいものを選びなさい。

文字列型: a ← "A"   // 大域変数

○ programC()
    a を出力          // 局所変数の宣言なし
    a ← "C"
    a を出力
解説を表示 正解:ウ
局所変数の宣言がないため、関数内の a はすべて大域変数を参照・変更します。
最初の出力時点では a="A" → "A" を出力。
その後 a ← "C" で大域変数が "C" に変わり、次の出力では "C" を出力します。

問題10-4 まとめ問題

次の手続 main を呼び出したとき、"X" が出力される回数として正しいものを選びなさい。

文字列型: msg ← "X"   // 大域変数

○ main()
    msg を出力
    文字列型: msg ← "Y"   // 局所変数として宣言
    msg を出力
    show(msg)
    change()

○ show(文字列型: value)
    value を出力
    文字列型: msg ← value   // 局所変数として宣言
    msg を出力

○ change()
    msg を出力             // 局所変数の宣言なし
    msg ← "Z"
    msg を出力
解説を表示 正解:イ
① main → 大域変数 msg="X" を出力:"X" ← 1回目
② main → 局所変数 msg ← "Y" を宣言。以降 main 内では "Y" が使われる
③ main → 局所変数 msg を出力:"Y"
④ show("Y") → value="Y" を出力:"Y" → show内の局所変数 msg="Y" を出力:"Y"
⑤ change() → 局所変数の宣言なし → 大域変数 msg="X" を出力:"X" ← 2回目
⑥ change() → 大域変数 msg ← "Z"(大域変数が "Z" に変わる)→ "Z" を出力

"X" が出力されるのは①と⑤の 2回
ポイント:main 内の局所変数 msg は main のスコープ内だけ有効。change() からは見えないため、change() は大域変数 msg="X" を参照します。