03 変数のスコープ

変数のスコープとは、その変数がどこで有効(アクセス可能)かを示す範囲のこと。C言語では大きく2種類ある。2級では「どこから参照できるか」「いつ初期化されるか」「同名のときどちらが優先か」、そして関数呼び出しごとの値の追跡(トレース)が問われる。

✅ この章のゴール:① ローカル変数=ブロック内だけ・呼ばれるたび再初期化 を説明できる ② グローバル変数=プログラム全体・一度だけ初期化 が分かる ③ 同名はローカル優先 を理解する ④ 関数呼び出しごとにグローバル変数がどう変わるかをトレースできる。

03-1 ローカル変数

ローカル変数は、関数やブロック { } の中で宣言され、その範囲内でのみ有効な変数である。関数の外からはアクセスできない。

void exampleFunction(void)
{
    int localVar = 10;  /* ローカル変数 */
    printf("localVar = %d¥n", localVar);
}

有効範囲(ブロック)

ローカル変数のスコープは、宣言されたブロック内に限定される。

{
    int a = 5;   /* このブロック内でのみ有効 */
}
/* a はここでは使用できない */

再初期化(呼ばれるたびに作り直される)

ローカル変数は、関数が呼び出されるたびに新しく作られ、初期化される。前回の値は残らない。

void exampleFunction(void)
{
    int counter = 0;  /* 毎回 0 で初期化 */
    counter++;
    printf("counter = %d¥n", counter);
}

int main(void)
{
    exampleFunction();  /* 出力: counter = 1 */
    exampleFunction();  /* 出力: counter = 1 (前回の値は残らない) */
    return 0;
}
⚠ 2回目も counter = 1 になる点に注意。ローカル変数は呼び出しごとに 0 に戻ってから ++ される。

03-2 グローバル変数

グローバル変数は、関数の外部で宣言され、プログラム全体で有効な変数である。どの関数からでもアクセスできる。

int globalVar = 20;  /* グローバル変数 */

void exampleFunction(void)
{
    printf("globalVar = %d¥n", globalVar);
}

有効範囲

グローバル変数のスコープは、宣言されたファイル全体に及ぶ(外部リンク時は複数ファイルにまたがることもある)。

再初期化(一度だけ)

プログラム実行中、グローバル変数は一度だけ初期化される。関数をまたいで値が共有・蓄積される。

int globalCounter = 0;

void incrementCounter(void)
{
    globalCounter++;
    printf("globalCounter = %d¥n", globalCounter);
}

int main(void)
{
    incrementCounter();  /* 出力: globalCounter = 1 */
    incrementCounter();  /* 出力: globalCounter = 2 (値が残る) */
    return 0;
}
ローカルは毎回リセット/グローバルは一度だけ初期化して値が残る。この違いが最頻出。先ほどの counter(毎回1)と、この globalCounter(1→2)を見比べること。

03-3 同名の変数(ローカル優先)

ローカル変数とグローバル変数で同じ名前の変数が存在する場合、その関数の中ではローカル変数が優先される(グローバル変数は隠れる)。

int value = 100;  /* グローバル変数 */

void exampleFunction(void)
{
    int value = 50;  /* ローカル変数(こちらが優先される) */
    printf("Local value = %d¥n", value);
}

int main(void)
{
    exampleFunction();
    printf("Global value = %d¥n", value);  /* main にはローカル value が無いのでグローバル */
    return 0;
}

実行結果:

Local value = 50
Global value = 100
exampleFunction 内の value = 50ローカル変数なので、グローバルの value = 100 は書き換わらない。main 側ではローカル value が無いため、グローバルの 100 がそのまま表示される。

03-4 トレースの考え方(関数呼び出しで値を追う)

2級では、グローバル変数と同名の仮引数を持つ関数を呼び出し、各行の出力を答えさせる問題が出る。コツは次の2点。

✅ ① 仮引数(関数の ( ) 内の変数)はローカル。グローバルと同名でも、関数内ではこの引数(=渡された値のコピー)を指す。関数内で引数を変えてもグローバルには影響しない。
✅ ② 関数内で「宣言していない名前」を使ったらグローバル。代入すればグローバルの値が変わり、次の呼び出しに持ち越される。

次のコードで実際に追ってみる。グローバルは x=2, y=3, z=4 で開始する。

#include <stdio.h>

int x = 2, y = 3, z = 4;   /* グローバル変数 */

int func1(int x)           /* 引数 x はローカル(グローバル x を隠す) */
{
    int y = 1;             /* ローカル y */
    x--;
    return x + y + z;      /* z はグローバル */
}

int func2(int y)           /* 引数 y はローカル */
{
    x += 4;                /* x はグローバル → 書き換わる */
    return x + y + z;
}

int func3(int z)           /* 引数 z はローカル */
{
    z += 7;                /* ローカル z だけ変わる */
    y -= 5;                /* y はグローバル → 書き換わる */
    return x + y + z;
}

int main(void)
{
    printf("%d¥n", x + y + z);   /* (15) */
    printf("%d¥n", func1(x));    /* (16) */
    printf("%d¥n", func2(x));    /* (17) */
    printf("%d¥n", func3(y));    /* (18) */
    printf("%d¥n", x + y + z);   /* (19) */
    return 0;
}

1行ずつトレース

呼び出し関数内での計算出力呼び出し後のグローバル
(15) x + y + z 2 + 3 + 4 9 x=2, y=3, z=4
(16) func1(x)
(x=2 を渡す)
引数 x=2(ローカル), ローカル y=1。x-- でローカル x=1。
return = 1 + 1 + 4(グローバルz)
6 x=2, y=3, z=4
(グローバル x は不変)
(17) func2(x)
(x=2 を渡す)
引数 y=2(ローカル)。x += 4グローバル x=6
return = 6 + 2 + 4
12 x=6, y=3, z=4
(グローバル x が増えた)
(18) func3(y)
(y=3 を渡す)
引数 z=3(ローカル)。z += 7 でローカル z=10。y -= 5グローバル y=-2
return = 6(グローバルx) + (-2) + 10
14 x=6, y=-2, z=4
(グローバル y が減った)
(19) x + y + z 6 + (-2) + 4 8 x=6, y=-2, z=4
⚠ ハマりどころ:
func1x--引数(ローカル)なのでグローバル x は変わらない。
func2x += 4宣言していない名前=グローバルなので x=6 に変わり、(19) まで残る。
func3z += 7引数(ローカル)なのでグローバル z は 4 のまま。一方 y -= 5グローバルなので y=-2 になる。

03-5 📝 過去問で確認

過去問(第59回 問3 変数のスコープ)— まず自分で解いてみよう
第59回 問3 変数のスコープ 問題①
第59回 問3 問題 ①
第59回 問3 変数のスコープ 問題②
問題 ②
解説を表示
第59回 問3 変数のスコープ 解説①
解説 ①
第59回 問3 変数のスコープ 解説②
解説 ②

03-6 まとめ


03-7 理解度チェック

Q1. 次のコードを実行するとどうなるか。

void exampleFunction(void)
{
    int x = 5;
    printf("x = %d¥n", x);
}

int main(void)
{
    exampleFunction();
    printf("x = %d¥n", x);   /* この行 */
    return 0;
}
解説を表示

正解:エ(コンパイルエラー)

ローカル変数 xexampleFunction の中だけで有効。main には x が宣言されていないため、mainprintf("x = %d", x) は「未宣言の識別子」でコンパイルエラーになる。

Q2. 次のコードの出力はどれか。

int x = 10;   /* グローバル変数 */

void exampleFunction(void)
{
    printf("x = %d¥n", x);
    x++;
}

int main(void)
{
    exampleFunction();
    exampleFunction();
    printf("x = %d¥n", x);
    return 0;
}
解説を表示

正解:ウ(10 / 11 / 12)

x はグローバル変数なので、関数をまたいで値が共有される。1回目の呼び出しで 10 を表示してから x++ で 11、2回目で 11 を表示してから 12。最後の main の出力は 12。

Q3. 次のコードの出力はどれか。

int value = 100;   /* グローバル変数 */

void exampleFunction(void)
{
    int value = 50;   /* ローカル変数 */
    printf("Local value = %d¥n", value);
}

int main(void)
{
    exampleFunction();
    printf("Global value = %d¥n", value);
    return 0;
}
解説を表示

正解:ア(Local value = 50 / Global value = 100)

exampleFunction 内では同名のローカル value = 50 が優先されるので 50 を表示。このローカル変数はグローバルを書き換えないため、main ではグローバルの value = 100 が表示される。

Q4. 下のコードの (15) の出力はどれか。

int x = 2, y = 3, z = 4;

int func1(int x) { int y = 1; x--; return x + y + z; }
int func2(int y) { x += 4; return x + y + z; }
int func3(int z) { z += 7; y -= 5; return x + y + z; }

int main(void)
{
    printf("%d¥n", x + y + z);   /* (15) */
    printf("%d¥n", func1(x));    /* (16) */
    printf("%d¥n", func2(x));    /* (17) */
    printf("%d¥n", func3(y));    /* (18) */
    printf("%d¥n", x + y + z);   /* (19) */
    return 0;
}
解説を表示

正解:オ(9)

開始時のグローバルは x=2, y=3, z=4。まだ関数を呼んでいないので x + y + z = 2 + 3 + 4 = 9

Q5. 同じコードの (16) func1(x) の出力はどれか。

解説を表示

正解:イ(6)

引数 x はローカルで 2 を受け取る。ローカル y = 1x-- でローカル x = 1z はグローバルの 4。よって 1 + 1 + 4 = 6。グローバル x は変わらない(2 のまま)。

Q6. 同じコードの (17) func2(x) の出力はどれか。(このとき渡す x はグローバルで 2)

解説を表示

正解:ウ(12)

引数 y はローカルで 2 を受け取る。x += 4xグローバルなので x = 6 になる。z はグローバルの 4。よって 6 + 2 + 4 = 12。以降グローバル x = 6 が残る。

Q7. 同じコードの (18) func3(y) の出力はどれか。(このとき渡す y はグローバルで 3、x はすでに 6)

解説を表示

正解:エ(14)

引数 z はローカルで 3 を受け取り、z += 7 でローカル z = 10(グローバル z は 4 のまま)。y -= 5yグローバルなので y = -2 になる。x はグローバルの 6。よって 6 + (-2) + 10 = 14

Q8. 同じコードの (19) x + y + z の出力はどれか。

解説を表示

正解:エ(8)

ここまでで書き換わったグローバルは x = 6(func2)と y = -2(func3)。z はグローバルでは 4 のまま(func3 で変わったのはローカル z)。よって 6 + (-2) + 4 = 8