C言語基礎|ブロック有効範囲

同じ名前でも、別モノとして安全に使える仕組み

C言語でコードを書いていると、「あれ、この変数名どこまで使えるんだっけ?」ってなる瞬間があります。
とくに関数を増やしたり、if や for の中で変数を宣言し始めると、名前の衝突が気になってきますよね。

そこで大事なのが 有効範囲(scope)
今回はその中でも、いちばん身近で登場回数が多い ブロック有効範囲(block scope) をやさしく押さえます。

有効範囲とは何か

有効範囲は一言でいうと、

その名前(識別子)が「通用する」範囲

です。
同じ tmp という名前でも、通用する範囲が別なら、別の変数として扱われます。

識別子に関する用語の整理

用語意味
識別子変数名・関数名などの名前total, tmp, power
有効範囲その識別子が使える範囲ブロック内だけ等
ブロック{ と } で囲まれたまとまり(複合文)if { ... }, for { ... }
ブロック有効範囲ブロック内で宣言した名前が通用する範囲宣言位置〜対応する }

この表は「scopeの話」をするときに混ざりやすい言葉を、最初に整理して迷子を防ぐためのものです。

サンプルプログラム

同じ名前 total を、別ブロックで使っても問題ないことを、出力の違いで体感できるプログラムです。

プロジェクト名:chap6-10-1 ソースファイル名:chap6-10-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

int main(void)
{
    int total = 0;  // main全体で使うtotal

    puts("買い物の合計金額を計算します。");

    // ブロック1:飲み物コーナー
    {
        int total = 0;  // ここで別のtotal(mainのtotalとは別物)
        int price;

        puts("【飲み物】3つの値段を入力してください。");
        for (int i = 1; i <= 3; i++) {
            printf("%dつ目の値段:", i);
            scanf("%d", &price);
            total += price;
        }
        printf("飲み物の小計:%d円\n", total);
    }

    // ブロック2:お菓子コーナー
    {
        int total = 0;  // これも別のtotal
        int price;

        puts("【お菓子】2つの値段を入力してください。");
        for (int i = 1; i <= 2; i++) {
            printf("%dつ目の値段:", i);
            scanf("%d", &price);
            total += price;
        }
        printf("お菓子の小計:%d円\n", total);
    }

    printf("全体の合計(mainのtotal):%d円\n", total);

    return 0;
}

実行例

買い物の合計金額を計算します。
【飲み物】3つの値段を入力してください。
1つ目の値段:120
2つ目の値段:150
3つ目の値段:130
飲み物の小計:400円
【お菓子】2つの値段を入力してください。
1つ目の値段:200
2つ目の値段:180
お菓子の小計:380円
全体の合計(mainのtotal):0円

最後が 0円 なのがポイントです。
ブロックの中の total は、main の total とは完全に別の変数なので、外には影響しません。

ブロック有効範囲は「宣言位置から } まで」

コードだけだと見落としやすいので、範囲を表にまとめます。

total が通用する範囲(イメージ)

変数宣言場所通用する範囲
mainのtotalmainの先頭mainの末尾まで(ただし内側で同名が出ると隠れる)
飲み物ブロックのtotal飲み物の { の中そのブロックの } まで
お菓子ブロックのtotalお菓子の { の中そのブロックの } まで

この表は「宣言された場所から、その宣言を囲むブロック終端の } まで」というルールを、範囲として見える化したものです。

“同名が出ると隠れる”という感覚(シャドーイング)

ブロックの中で同じ名前を宣言すると、外側の同名は一時的に見えなくなります。
これを 隠蔽(シャドーイング) と呼びます。

同名があるとき、どれが使われる?

場所total と書いたら、どれ?
ブロックの外(main直下)mainのtotal
飲み物ブロックの中飲み物ブロックのtotal
お菓子ブロックの中お菓子ブロックのtotal

つまり、同じ名前を使っても動くけど、読み手が混乱しやすいので、実務では「わざと同名」は避けることが多いです(ただし学習では仕組みを理解するのに超有効です)。

登場する命令の書式と「何をする命令か」

この記事で出てきたものを、ブロック有効範囲の理解につながる形でまとめます。

複合文(ブロック)の書式

  • { 文の並び }

何をする?
複数の文をひとまとまりにするための構文です。
この中で宣言した変数は、そのブロックの中だけで使えるのがポイントです。

変数宣言の書式

  • 型 変数名;
  • 型 変数名 = 初期値;

何をする?
変数を作ります。ブロック内で宣言すると、その変数名はブロック内だけに限定されます。

for 文の書式

  • for (初期化; 条件; 更新) 文

何をする?
繰り返し処理をします。
今回の例では「入力を複数回読む」ために使っています。

補足:C99以降だと for の初期化で int i を宣言できます。
この i も for の中(正確にはそのブロック)だけで通用します。

printf / puts / scanf の書式

  • printf(書式文字列, 値…)
  • puts(文字列)
  • scanf(書式文字列, 変数のアドレス…)

何をする?
表示と入力です。ブロック有効範囲そのものを作る命令ではないですが、結果を観察するために使います。

演習問題

演習6-7

画面に「こんにちは。」と表示する関数を作成せよ。
仕様:引数なし、返却値なし

  • 関数プロトタイプ:void hello(void);

解答例

プロジェクト名:chap6-10-2 ソースファイル名:chap6-10-2.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

#include <stdio.h>

void hello(void)
{
    puts("こんにちは。");
}

int main(void)
{
    hello();
    return 0;
}

解説

  • hello は値を返さないので返却値型は void
  • 引数を受け取らないので (void) と書く
  • main から hello() として呼び出す

重要ポイントのまとめ(ここだけ覚えればOK)

ブロック有効範囲の要点

ルールひとこと
ブロック内で宣言した名前はブロック内だけ外に出ると使えない
使えるのは宣言した行から } まで宣言より前では使えない
内側で同名を宣言すると外側は隠れるどっちの変数か注意