スコープ

スコープとは?

変数や関数を宣言、定義した後、その変数が使用できる範囲をスコープといいます。変数には大きく分けて、グローバル変数とローカル変数の 2 種類があります。関数の中で宣言する変数 (引数を含む) をローカル変数と言い、関数の外で宣言する変数をグローバル変数といいます。C 言語では、ローカル変数の宣言はブロック { } の始まりにしか書くことができません。逆に言えば、ブロックがあれば関数の始めでなくとも if や for などによってできるブロックの始めでローカル変数を宣言することができます。ただし、ローカル変数のスコープはその変数が宣言されたブロック内に限られ、一旦ブロックから抜けるとそのブロック内で宣言されたローカル変数は使えなくなります。このように、ごく一部でしか使わない変数はそれ以外の場所で使えなくすることによって混乱を避けようとするのが、C 言語をはじめ近代的なプログラミング言語の考え方です。if や for などの特定のステートメントがなくても、任意の場所にブロック記号 { } を書いてスコープを作ることもできます。グローバル変数は、基本的にどの関数からも使うことができます。ただし、関数への引数などで実現できることは、できるだけグローバル変数を使わないようにしたプログラムが好ましいとされています。スコープの同一階層で同じ名前の変数を複数宣言することはできませんが、スコープの階層が下がると同じ名前の変数を使うことができます。この場合、重複する名前の変数は最も深い階層で宣言された変数への参照とみなされます。次の例を見てください。

重複する識別子の解決

int test = 1;  //  グローバル変数

/*
 *  エントリーポイント
 */
void main(){
    int test = 2;  //  ローカル変数
    {  //  スコープを作る
        int test = 3;  //  ローカル変数
        {  //  スコープを作る
            int test = 4;  //  ローカル変数
            SysDialog(test);
        }
        SysDialog(test);
    }
    SysDialog(test);
    func();  //  別の関数を呼ぶ
}

/*
 *  別の関数
 */
void func(){
    SysDialog(test);
}

解説

このサンプルでは、関数内にブロックを作ることによりローカルスコープの階層を深くしています。各所で参照される test という名前の変数がどの test を指しているか、確認してください。

記憶クラス

ローカル変数は、一旦スコープに入ると使えるようになり、スコープを抜けると内容の保障はなくなります。たとえば、ある関数がローカル変数に何か値を代入して終了したとします。次に同じ関数が呼ばれたとき、そのローカル変数に前回代入しておいた値が残っているという保障は全くありません。このようなローカル変数は、スコープに入るたびに領域の割り当てが行われるので自動変数 (auto) といいます。グローバル変数は基本的にプログラムが起動して終了するまで値が残っています。それでは、関数が終了しても値を残しておきたい変数をすべてグローバルにする必要があるかというとそうではなく、ローカル変数のまま値を残しておくようにする機能があります。それは、静的変数 (static) というもので、ローカル変数を静的にするとグローバル変数と同様、プログラムが終了するまで値が保持されます。このように、変数の値が保持される期間の種類を記憶クラスといいます。ただし、Queek II では一般の C 言語と異なり、セーブデータのロードやニューゲームの開始などのたびグローバル変数や static 変数が初期化されます。これは、RPG というゲームの特性上、セーブデータの単位でグローバル変数や static 変数の寿命を定めた方が好ましいと考えるからです。ただし、セーブデータの単位を超えて値を保持するための globalsave という機能もあります。詳しくは後述します。本題に戻り、static なローカル変数を宣言するためには、型名の前に static と書きます。前述した const 定数は常に static として扱われます。なぜなら、定数の値はプログラム開始から終了まで絶対に変わることはないからです。const や static として宣言されなかったローカル変数はすべて auto 変数になります。auto というキーワードは書いてもいいですが、普通書く人はいません。では、記憶クラスの違いによるデータの寿命をテストするサンプルスクリプトを見てください。

記憶クラスのテスト

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

/*
 *  エントリーポイント
 */
void main(){
    func();  //  関数を 3 回実行
    func();
    func();
}

/*
 *  別の関数
 */
void func(){
    static int test2 = 20;  //  静的ローカル変数
    int test3 = 30;         //  自動ローカル変数
    //  3 つの変数を表示
    SysDialog("test1: "+test1+"\ntest2: "+test2+"\ntest3: "+test3);
    test1++; test2++; test3++;  //  3 つの変数をそれぞれインクリメントする
}

解説

実行結果はどのようになりましたか?グローバル変数と static 変数の値は増加し、自動変数の値は常に同じでした。static 変数の初期化式はプログラム開始時に 1 回だけ実行されますが、自動変数の初期化式はスコープに入るたびに毎回実行されます。static 変数の初期化式の右辺は、const 定数の初期化と同じく定数でなければなりません。

ファイルスコープ

static という記憶クラスをローカル変数に使うことで、データの寿命が変化することを先のサンプルで示しました。static 記憶クラスは、グローバル変数や関数に対しても使えるのですが、ローカル変数に対し使った場合とは少し意味が異なってきますので注意してください。グローバル変数や関数に対して static 記憶クラスを指定すると、それらの変数や関数は、宣言・定義されているスクリプトファイルの中でしか使用できなくなります。これをファイルスコープといいます。非静的なグローバル変数や関数は他のスクリプトファイルでも使うことができ、また、識別子 (名前) が重複してはいけません。静的なグローバル変数や関数は、別のスクリプトファイルに存在するもの同士であれば同じ識別子を使うことができます。なお、一般の C 言語では、非静的なグローバル変数や関数であっても、他のソースファイルで使用するためにはすでに述べたプロトタイプ宣言をしたり、グローバル変数については外部リンケージ (extern) の宣言というものが必要になってくるのですが、Queek C では簡単のためこれらの宣言は不要となっています。それでは本題に移り、ファイルスコープのテストを行うサンプルスクリプトを見てみましょう。ファイルスコープをテストするためには別のスクリプトファイルを作らなければなりません。ここでは、"Test.qc" という新たなスクリプトファイルを作成して、それぞれのスクリプトファイルに以下のようなソースコードを入力してコンパイルし、実行してみてください。その際、「"Global.qs" が壊れています。」という警告が表示されますが、気にしなくてもいいです。

"Main.qc"

int value1 = 10;  //  非静的グローバル変数

static int value2 = 20;  //  静的グローバル変数

/*
 *  エントリーポイント
 */
void main(){
    Test();  //  他のファイルにある非静的関数を呼ぶ
    Func();  //  静的関数を呼ぶ
}

/*
 *  静的関数
 */
static void Func(){
    //  非静的グローバル変数と静的グローバル変数を表示する
    SysDialog("at \"Main.qc\"\nvalue1: "+value1+"\nvalue2: "+value2);
}

"Test.qc"

static int value2 = 30;  //  静的グローバル変数

/*
 *  別のファイルスコープにある非静的関数
 */
void Test(){
    Func();  //  静的関数を呼ぶ
}

/*
 *  別のファイルスコープにある静的関数
 */
static void Func(){
    //  非静的グローバル変数と静的グローバル変数を表示する
    SysDialog("at \"Test.qc\"\nvalue1: "+value1+"\nvalue2: "+value2);
}

解説

Test という関数は、main 関数とは別のファイルスコープに存在しますが、非静的関数なので main からも呼ぶことができます。また、Test と main の両関数内で Func という静的関数を呼んでいますが、これらはファイルによって別々のものが呼び出されているという点に注意してください。また、value1, value2 というそれぞれ非静的、静的なグローバル変数を Func 内で表示していますが、value1 はどちらも同じ値で表示されているのに対し、静的な value2 はそれぞれのファイルによって異なる変数が参照されている点に注意してください。なお、関数は静的であっても定義より前で使用できますが、const や static の記憶クラスで宣言されたグローバル変数 (定数) は同一ファイル内でも宣言より後でしか使用できないので注意してください。また、ローカルな const 定数は static 記憶クラスを持ちますが、グローバルな const 定数はデフォルトで非静的なので注意してください。グローバルな const 定数に static 記憶クラスを持たせるためには、明示的に宣言を行う必要があります。また、ローカルな const 定数や static 変数の初期化には任意の定数を使用できますが、グローバルな const 定数の初期化に使用できるのは同一ファイル内ですでに宣言された const 定数だけです。これは、定数の初期化に相互参照性をなくすためです。

<PrevPage | ▲SecTop | ▲PageTop | NextPage>
Copyright (c) 1999-2006 インターネット停留所