学校のクラスでしか通じないあだ名があります。教室の中では誰にでも通じるのに、一歩出ると誰も知らない。変数にもこれとまったく同じ性質があり、名前が通じる範囲のことをスコープと呼びます。
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 か」を常に気にしなければなりません。スコープは、考える範囲を機械的に狭めてくれる仕組みです。変数名を見つけたら、いちばん内側のブロックから外へ順に探す——コンパイラも人間も、同じ順番で名前を解決します。
罠・注意
- 「見えない」と「無くなった」は別物です。
subの実行中もmainのnは生きていて、ただ見えないだけです(寿命の話は次のトピック)。 - 変数の宣言は「使う直前・いちばん内側」に置くのが現代的な書き方です。関数の先頭にすべて並べる古い流儀は、スコープを無駄に広げます。
- スコープの外にある変数を使おうとすると、コンパイラは「宣言されていない識別子」というエラーを出します。綴りの間違いと同じメッセージなので、まず「その名前はここから見えるか」を確認してください。
まとめ: スコープは { } 単位。狭く保つほど、名前の衝突も、うっかりの書き換えも起きなくなります。次のトピックでは、これとよく似て非なる「寿命」の話に進みます。