ローカル変数
ローカル変数(局所変数、英: local variable)とは、プログラムの一部分でしか利用できない変数のことである。一般的にグローバル変数(大域変数)と対比される。ローカル変数の定義はプログラミング言語によって異なるので、詳細な説明は言語別の項に譲る。
C言語
[編集]C言語およびC++のローカル変数は、関数内の同じ「ブロック」という領域内からのみ参照可能な変数である。C99よりも前の規格ではブロックの先頭部分でのみ定義可能だったが、C99からはC++同様に任意の位置で定義可能である。
ある関数fにおけるローカル変数aのスコープ (scope) すなわち可視性は、その関数f内において、その変数aが宣言された場所から、その変数aが定義されたブロックを抜けるまでである。スコープ外からは参照することができない。ローカル変数aを定義したブロック内で別の関数gを呼び出すと、関数gからはその変数aが見えなくなるが、メモリ上には依然として存在しており、関数gが終了して関数fに戻り、変数aのスコープに入ると再び見えるようになる。
下記の例において、2行目で宣言されているローカル変数i
のスコープは2行目から6行目の中括弧までであり、4行目で宣言されているローカル変数j
のスコープは4行目から5行目の中括弧までである。
void Function() {
int i = 1; /* スコープの広いローカル変数 */
{
int j = 1; /* スコープの狭いローカル変数 */
}
}
C言語のローカル変数の寿命 (lifetime) は、デフォルトではそのローカル変数を定義したブロックを抜けるまでである。ローカル変数は宣言により自動的にメモリ割り当てが行なわれるが、ブロックを抜けて寿命が尽きると自動的にメモリ割り当てが解除される。また、ローカル変数が定義された関数の制御フローが呼び出し元に返り、コールスタック領域が解放されると、そのローカル変数のメモリ領域は自動的に解放される。したがって「自動変数」[1]とも呼ばれる。未初期化の自動変数の内容は不定 (indeterminate) である[2]。
ローカル変数宣言にstatic
キーワード(静的記憶クラス指定子)を付加すると、「静的ローカル変数」[3]となり、変数寿命はプログラムの生存期間と同一となる。C言語では静的ローカル変数はグローバル変数と同じくプログラム開始処理以前に一度だけ初期化されるが、C++では制御フローが静的ローカル変数の定義箇所に到達した際にその初期化式が一度だけ評価され、その値によって変数は一度だけ初期化される[4]。
C99の例を示す。なお、下記(1)はC言語およびC++03規格までのC++ではauto int a = 0;
と等価だが、C++11以降はauto
キーワードの意味が変更されたため、等価ではなくなった。
#include <stdio.h>
int Function1(void) {
int a = 0; // (1) 通常のローカル変数(自動変数)の宣言と定義。初期化は関数を呼び出すたびに毎回行なわれる。
a += 1;
return a;
}
int Function2(void) {
static int s = 0; // (2) 静的ローカル変数の宣言と定義。初期化は一度だけ行なわれる。
s += 1;
return s;
}
int main(void) {
for (int i = 0; i < 5; ++i) {
printf("auto(%d) = %d\n", i, Function1());
printf("static(%d) = %d\n", i, Function2());
}
return 0;
}
以下のコードはC++ではコンパイル可能だが、C言語では静的ローカル変数はコンパイル時定数で初期化されなければならないため、コンパイル不可能である。
#include <stdio.h>
int Func(int x) {
if (x > 0) {
static int firstPositive = x;
return firstPositive;
} else if (x < 0) {
static int firstNegative = x;
return firstNegative;
}
return 0;
}
int main(void) {
printf("%d\n", Func(1)); // 1
printf("%d\n", Func(5)); // 1
printf("%d\n", Func(-5)); // -5
printf("%d\n", Func(-1)); // -5
printf("%d\n", Func(0)); // 0
return 0;
}
C++ではSingleton パターンの実現に静的ローカル変数が利用されることがある。
また、C++ではローカル変数の寿命が尽きるときにデストラクタが呼ばれる。この性質を利用したデザインパターンがRAIIである。
Java
[編集]配列の要素は「インスタンス変数」に分類される。メソッド、コンストラクタ、catch節の引数は「仮引数」に分類される。
Javaにおいては、メソッド内で宣言されている変数をローカル変数と呼ぶ。スコープの概念およびブロックの構文はC/C++とほぼ同様だが、C/C++と異なり、静的ローカル変数は定義できない。フィールド(インスタンス変数とクラス変数)に関しては、コンパイラが型に応じて適切な既定値を割り当てるが、ローカル変数に関しては既定値は割り当てられない[6]。
下記の例において、3行目で宣言されているローカル変数i
のスコープは3行目から7行目の中括弧までであり、5行目で宣言されているローカル変数j
のスコープは5行目から6行目の中括弧までである。
class Foo {
void bar() {
int i = 1; // スコープの広いローカル変数
{
int j = 1; // スコープの狭いローカル変数
}
}
}
以下のような未初期化のローカル変数にアクセスするコードは、C/C++では未定義動作を引き起こすものの、コンパイルは通ってしまう。一方、Javaではコンパイルエラーとなる。
int x;
if (x == 0) {
}
Javaのメモリ解放はガベージコレクションによって行なわれる。Javaのローカル変数はスタック領域に確保されるが、クラスや配列などの参照型の場合、オブジェクト本体はヒープ領域に作成される。ローカル変数の寿命が尽きて、その変数が指していたオブジェクトがまったく参照されなくなったとしても、そのオブジェクトのメモリ領域が直ちに解放されるとは限らない。
{
int[] a = new int[100];
}
// ブロックを抜けると変数 a は削除されるが、a が指していた配列オブジェクト本体の削除はガベージコレクタが担当する。
脚注
[編集]- ^ C言語の規格では「自動記憶域期間 (automatic storage duration) を持つオブジェクト」と表現されている。
- ^ ISO/IEC 9899:1999, §6.2.4 Storage durations of objects
- ^ C言語の規格では「静的記憶域期間 (static storage duration) を持つオブジェクト」と表現されている。
- ^ C++11よりも前の規格では、静的ローカル変数の初期化はスレッドセーフ性が保証されない。そのため、複数のスレッドから同時に初回アクセスが発生した場合、未定義動作を引き起こす。
- ^ Variables (The Java™ Tutorials > Learning the Java Language > Language Basics)
- ^ Summary of Variables (The Java™ Tutorials > Learning the Java Language > Language Basics)