4.記憶クラス

4-4. スコープ

◆スコープ

スコープ(可視性)には以下4つありますが今回はその中の2つだけ解説します。

1.ファイルスコープ:翻訳単位内全域
2.ブロックスコープ:ブロック内
3.関数スコープ  :関数内全域
4.関数原型スコープ:関数宣言のみ

まずは、スコープそのものについて説明します。

スコープというのはオブジェクトの有効(利用)範囲のことでこれは静的・自動関係なく同じ制限を受けます。(ここでは具体性を出すためにオブジェクトといっていますが対象になるのはもっと広く正確には識別子※全体に適用されます。)

※識別子
あらかじめC言語で用意されている予約語(int for if など)以外でプログラム内で新たに定義した名前を識別子という。
変数や配列などのオブジェクトだけでなく、変数やラベルなども識別子に含まれる。

ただし上記のexternはそれを結合してしまうので翻訳(コンパイル)単位は別でも同じオブジェクトを使えるようにしています。(ちなみにexternの対象となったオブジェクトは敢えて言うならグローバルスコープになります)

また#includeはコンパイル時に指定したファイルを挿入して1つのファイルのように扱うので翻訳単位は同じになります。(ファイルスコープが同じ)

それでは、ファイルスコープが1つの翻訳単位で有効なのはわかりましたが具体的にはどのオブジェクトがファイルスコープの対象になるでしょうか?

/* begin */
int  i;
char c;

main()
{
    int arr[]={1,2,3,4,5};
}
/* end of file */

答えは、グローバル変数の i と c がファイルスコープの対象になります。
どちらもファイル全域で使用可能なので間違いないですね。

次に、ブロックスコープ。ブロックとは{中括弧}で囲まれた領域のことです。
上のプログラムでは配列 arr がmain関数のブロック内で有効となります。

もちろん有効といってもコンパイラの性質上宣言より上の行では使えませんが

main()
{
    arr[3]=3;  /* 宣言より上で使ってはダメ! */

    int arr[]={1,2,3,4,5};
}

また、ブロックスコープであるarrはローカルオブジェクトになりmain関数の外側で使用することができません。

/* begin */
arr[2]=4;  /* もちろんここでは使えない */

main()
{
    int arr[]={1,2,3,4,5};
}

arr[4]=12; /* 宣言の下であっても関数の外なのでダメ */

/* end of file */

あくまでブロックの中なので関数に限定するのではなく次の場合もブロックスコープです。

main()
{
    int i;
    for(i=0;i<5;i++){
        static int a=3;  /* forの中で有効な変数 a */
        a++;
    }

    {
    char c='A';  /* ただブロックで囲んだけの複文もブロックスコープ */
    int b=0;
    }
}

◇別スコープの同じ識別名

まずは、識別名の定義ついて基本的には同じ名前のオブジェクトなどを二つ以上定義すると二重定義としてコンパイルエラーになりますが、スコープが別であればこの限りではありません。

main()
{
    int i=0;
    int i=0;  /* 二重定義なのでエラーになる */
}

fn()
{
    int i=0;  /* ここのiはmainとスコープが
                 別なので二重定義にならない */
}

mainのiとfnのiはスコープが違うので別物として扱われます。
main実行中ではmainのi、fn実行中のときはfnのiを使用します。

それでは、次の場合はどうでしょう

◇スコープの優先度

int i=0;

main()
{
    int i;
    i=2;
    while(i<5){
        int i;
        i=10;
    }

    for(i=0;i<5;i++){
        i++;
        i--;
    }

}

またまた厄介な話になりました。確かにスコープは別ですが…

コンパイルしてもエラーは出ないと思いますがプログラム中の各iはどのiでしょうか?

まず、i=2; の文 このiはグローバルのiかそれともmainのiか?
そしてi=10; このiはどのi?
さらに最大の疑問はwhileとforの括弧の中のiです。

さっき確かに別のスコープといいましたがスコープの範囲は重なっているので、
どちらが優先されるのか決まっていないと困ります。

答えから言いますとローカルになるほど優先度が高いことになります。
つまり、現在実行している場所でグローバルオブジェクトとローカルオブジェクトの両方のスコープに含まれている場合ローカルのほうが優先されると言うことです。

上のプログラムに答えを書き足したものを下に示します。

int i=0;         /* グローバル i */

main()
{
    int i;       /* main の i */
    i=2;         /* main の i */
    while(i<5){
        int i;   /* while の i */
        i=10;    /* while の i */
    }

    for(i=0;i<5;i++){
        i++;     /* main の i */
        i--;     /* main の i */
    }

}

となります。最後にwhileとforの括弧の中ですがここはmain関数のスコープなのでmainのiになります。

次章
5.演算子