トラック7・🧩関数とスコープ

変数のスコープ——見える範囲

読む目安 約12分・ゴール:ブロックごとのスコープを理解し、変数がどこから見えるか判断できる

アプリでこのトピックを学ぶ(無料・登録不要)

このトピックの要点

学校のクラスでしか通じないあだ名があります。教室の中では誰にでも通じるのに、一歩出ると誰も知らない。変数にもこれとまったく同じ性質があり、名前が通じる範囲のことをスコープと呼びます。

Cのスコープの単位はブロック、つまり { } です。ブロックの中で宣言した変数は、その } に到達した瞬間に消えます。

#include <stdio.h>

void sub(void)
{
    int n = 100;       // sub の中だけの n
    printf("sub: %d\n", n);
}

int main(void)
{
    int n = 1;         // main の中だけの n
    sub();
    printf("main: %d\n", n);   // 1(subの100とは無関係)
    return 0;
}

同じ n という名前でも、別の関数のものはまったく別の変数です。だから関数を作るときに「他の関数で使っている名前とぶつからないか」を心配する必要はありません。これが関数を安心して部品にできる理由のひとつです。

ブロックはもっと細かい

関数の中でも { } があればそこがスコープになります。for文の初期化部で宣言した変数も、ループの中だけの命です。

#include <stdio.h>

int main(void)
{
    for (int i = 0; i < 3; i++) {
        int sq = i * i;
        printf("%d ", sq);
    }
    // printf("%d\n", i);   ← エラー。iはもう存在しない
    printf("\n");
    return 0;
}

i も sq もループを抜けた時点で消えています。ループの結果を後で使いたいなら、ループの外で宣言した変数に入れておく必要があります。合計を数える sum をループの中で宣言してしまい、毎回 0 に戻って合計が合わない——これは初学者が必ず一度は通る失敗です。「値を残したい変数は、ループより外」と覚えておきましょう。while文でも事情はまったく同じです。

同じ名前を隠す——シャドーイング

#include <stdio.h>

int main(void)
{
    int x = 1;
    {
        int x = 99;          // 外側のxを隠す
        printf("%d\n", x);   // 99
    }
    printf("%d\n", x);       // 1(外側は無事)
    return 0;
}

内側のブロックで外側と同じ名前を宣言すると、その中では内側の x だけが見え、外側の x は隠れます。これをシャドーイングと呼びます。文法上は合法で、コンパイラも黙って通します。しかし読む人は「どちらの x か」を毎回考えることになり、書き換えたつもりが別の変数だった、という事故につながります。名前を変えるだけで防げるので、避けるのが賢明です。

なぜ範囲を区切るのか

もしすべての変数がどこからでも見えたら、100行のプログラムでも「この i は誰の i か」を常に気にしなければなりません。スコープは、考える範囲を機械的に狭めてくれる仕組みです。変数名を見つけたら、いちばん内側のブロックから外へ順に探す——コンパイラも人間も、同じ順番で名前を解決します。

罠・注意

師範見える範囲は、狭ければ狭いほどよい。触れぬものは壊せぬからのう

まとめ: スコープは { } 単位。狭く保つほど、名前の衝突も、うっかりの書き換えも起きなくなります。次のトピックでは、これとよく似て非なる「寿命」の話に進みます。

アプリでこのトピックを学ぶ(無料・登録不要)

確認問題

確認問題 1

変数のスコープについて、正しい説明はどれですか。

  1. 関数Aのローカル変数は、関数Bからも同じ名前で使える
  2. for の初期化部で宣言した変数は、ループを抜けた後も使える
  3. ブロック { } の中で宣言した変数は、その } を抜けると使えなくなる
  4. 同じ名前の変数は、プログラム全体で1つしか作れない
答えを見る

正解:C(ブロック { } の中で宣言した変数は、その } を抜けると使えなくなる)

Cのスコープの単位はブロック { } です。別の関数のローカル変数は見えず、for の初期化部で宣言した変数もループ内限定です。だからこそ、関数ごとに好きな名前を安心して使えます。

確認問題 2

次のプログラムの出力はどれですか。

#include <stdio.h>

void sub(void)
{
    int n = 100;
    n++;
}

int main(void)
{
    int n = 1;
    sub();
    printf("%d\n", n);
    return 0;
}
  1. 1
  2. 101
  3. 100
  4. 2
答えを見る

正解:A(1)

sub の n と main の n は名前が同じでも別の変数です。sub の中でいくら書き換えても main の n には影響しません。sub を抜けた時点で sub の n は消えます。

「関数とスコープ」の目次

  1. 関数とは——処理に名前を付ける
  2. 引数と戻り値
  3. プロトタイプ宣言
  4. 変数のスコープ——見える範囲
  5. 変数の寿命とstatic
  6. コールスタック——関数呼び出しの舞台裏
  7. 値渡しの本質
  8. グローバル変数との付き合い方
  9. 関数分割の設計センス
  10. 再帰の入口——自分を呼ぶ関数

もっと先へ:ポインタ・メモリ・ファイル入出力・セキュアコーディングを含む全26トラックと、段位検定・模試のフルセットは完全版に収録しています。

完全版の販売ページは準備中です。

ほかのトラック

📚 姉妹教材:手を動かして覚える Linux 教材 — Linuxとインフラの仕組みを地図で学ぶ

Web版