変数は、変数を定義する場所によって、その変数の使える場所(通用範囲)が異なります。通用範囲のことを、スコープとも呼びます。
ローカル(局所)変数とは、関数の中で定義された変数のことです。ローカル変数のスコープは、その関数(正確にはブロック)の中になります。
int func(int x) { int sum = 0; for (int i=0; i<10; i++) { int y = x + i; sum = sum + y * y; } y, i の通用範囲ここまで // sum = sum + y * y; ←コンパイルエラーになる // return i; ←コンパイルエラーになる return sum; } sum, x の通用範囲ここまで int other(int y) { // return x; ←コンパイルエラーになる // return sum; ←コンパイルエラーになる return y + 5; }
sum という変数は、関数 func の中で定義してありますので、この関数の中でのみ使えます。関数 other では使えません。y という変数はもちろん関数 func の中で使えるのですが、さらに内側の for ループで定義されているので、for ループの中でのみ使えます。
関数の仮引数は、その関数の内側のブロックで定義されたものと解釈されます。したがって、関数 func の x は関数 other では使えません。for ループで定義された変数 i も、ループのブロックのみで用いることができます。
グローバル(大域)変数とは、関数の外で定義された変数のことです。関数をまたいで共通に使えます。
int a = 0; void add2(void) { a = a + 2; } void add5(void) { a = a + 5; } int main() { printf("%d\n", a); // 0 add2(); printf("%d\n", a); // 2 add5(); printf("%d\n", a); // 7 }
a という変数は関数の外側で定義されているので、main 関数でも使えますし、add2, add5 関数でも使えます。
グローバル変数は、一見したところ便利なように思えます。関数の引数を用いることなく、値の受け渡しができますが、大きな副作用を伴います。変数名は重複することができませんので、グローバル変数が増えてくると変数名が重複しないように命名するのが困難になります。そして動作不良を起こしたときに、プログラム上の疑うべき処理が飛躍的に増加し、デバッグが困難になります。スコープを狭くしておくことが、信頼性の高い、よいプログラムを書くコツです。
変数名は、同じブロックでは重複させることができません。
void func(void) { int a; double a; // コンパイルエラー }
スコープが異なると、変数名が重複してもよくなります。異なる変数として扱われます。
void func(void) { int x = 0; printf("%d", x); } // 0 void other(void) { int x = 5; printf("%d", x); } // 5
スコープが入れ子になっている場合は、内側のスコープの変数が優先されます。
int a = 5; // グローバル変数 int func(void) { printf("%d\n", a); // 5, グローバル変数 } int other(void) { int a = 10; // ローカル変数 printf("%d\n", a); // 10, ローカル変数 }