変数のスコープ
関数の中の変数は外から見えない
関数を分けはじめると、ほぼ全員が一度は次のコードを書きます。
void show_total(void) {
printf("合計は%d点です\n", total); /* total は main のもの */
}
int main(void) {
int total = 255;
show_total();
return 0;
}コンパイルすると use of undeclared identifier 'total' と怒られます。main の中に確かに total はあるのに、show_total からは見えません。
変数が使える範囲のことを スコープ と呼びます。関数の中で宣言した変数は ローカル変数 で、そのスコープは宣言した波カッコの中だけです。main も1つの関数なので、main で宣言した変数も main 専用です。特別扱いはありません。
直し方は、見せたい値を引数として渡すことです。
void show_total(int total) {
printf("合計は%d点です\n", total);
}
int main(void) {
int total = 255;
show_total(total);
return 0;
}関数が必要とする値を引数リストに並べておくと、その関数だけを読めば「何を材料にしているか」が分かります。外の変数を勝手に読める仕組みだと、遠くの行を書き換えた影響がどこに出るか追えなくなります。見えないのは不便な制限ではなく、追跡できる範囲を狭く保つための仕組みです。
同じ名前でも別の変数
スコープが分かれているということは、違う関数で同じ名前を使っても衝突しないということです。
int add_bonus(int score) {
int total = score + 5;
return total;
}
int main(void) {
int total = 0;
total = total + add_bonus(70);
printf("%d\n", total); /* 75 */
return 0;
}add_bonus の total と main の total は、名前が同じだけの完全に別の変数です。add_bonus の中で total を 75 にしても、main の total は 0 のままで、変わるのは return された値を足したときだけです。
これは第2章で学んだ、引数が値のコピーで渡されるという話とつながっています。関数どうしは値のやりとりだけでつながっていて、変数そのものを共有してはいません。
生まれて消えるタイミング
ローカル変数は、関数が呼ばれたときに作られ、関数が終わると消えます。add_bonus を3回呼べば、total は3回作られて3回消えます。前の呼び出しの値が残ることはないので、関数の頭で初期化を忘れても前回の続きから、ということは起きません。逆に言えば、値を次の呼び出しへ持ち越したいなら、戻り値で返して呼び出し元が持っておく必要があります。
なお、for のカウンタのように波カッコの中で宣言した変数は、その波カッコを抜けた時点で見えなくなります。スコープの単位は関数ではなく波カッコだ、と覚えておくと正確です。
では、同じ名前の変数を2つの関数に持たせて、混ざらないことを確かめましょう。
要件
- add_bonus は点数を受け取り、5 を足した値を返す関数にする
- add_bonus の中では total という名前のローカル変数に計算結果を入れてから return する
- main でも total という名前の変数を用意し、0 で初期化して補正後の点数を足していく
- for 文で3人分の点数を scanf で読み込み、1人目 75点 の形で1行ずつ表示する
- 最後の行に 合計は255点です の形で main の total を表示する
入出力例
main("70 80 90") → "1人目 75点
2人目 85点
3人目 95点
合計は255点です"
main(40, 55, 60) → "1人目 45点
2人目 60点
3人目 65点
合計は170点です"