C言語のきほん|ローカル変数とグローバル変数のスコープ

変数がどこまで見えるかを理解すると、C言語のコードはもっと整理しやすくなる

C言語でプログラムを書いていると、
「この変数はなぜここで使えるのか」
「同じ名前なのに、どうして別の値として扱われるのか」
「関数の中で宣言した変数は、外ではなぜ使えないのか」
といった疑問に出会うことがあります。

こうした疑問を整理するために大切なのが、スコープ 有効範囲 の考え方です。
スコープとは、変数や関数の名前をどこまで使えるかを表すルールのことです。

特にC言語では、変数は大きく分けると

  • ローカル変数
  • グローバル変数

の2種類として理解すると、とても分かりやすくなります。

ローカル変数は、関数の中やブロックの中でだけ使える変数です。
一方、グローバル変数は、関数の外で宣言され、より広い範囲から使える変数です。

この違いをしっかり理解しておくと、

  • 変数名の重複がどう扱われるか
  • どの変数がどこで有効か
  • なぜある場所では使えて、別の場所では使えないのか

がすっきり見えてきます。

ここでは、ローカル変数とグローバル変数のスコープの違いを、図の考え方に沿って、具体例を交えながらやさしく整理していきます。

スコープ 有効範囲 とは

スコープとは、その名前を使える範囲 のことです。
変数でも関数でも、宣言した場所によって「見える範囲」が決まります。

今回特に大事なのは、次の2つです。

種類内容
ブロックスコープ{ } で囲まれたブロックの中で有効
ファイルスコープ宣言位置以降、そのソースファイル内で有効

この2つをもとに考えると、

  • ブロックスコープを持つ変数 → ローカル変数
  • ファイルスコープを持つ変数 → グローバル変数

という対応で理解できます。

ローカル変数とは

ローカル変数とは、関数の中や、for 文や if 文などのブロックの中で宣言された変数のことです。
その変数は、宣言されたブロックの中でだけ使えます。

たとえば、次のような変数です。

int main(void)
{
    int count = 3;
    return 0;
}

この count は main 関数の中だけで有効です。
別の関数から count を使うことはできません。

ローカル変数の特徴を短くまとめると、次のようになります。

項目内容
宣言場所関数の中、またはブロックの中
有効範囲宣言されたブロックの中だけ
外から見えるか見えない

グローバル変数とは

グローバル変数とは、関数の外で宣言された変数のことです。
こうした変数は、そのソースファイルの中で広い範囲から使えます。

たとえば、

int g_score = 80;

のように関数の外で宣言すると、これがグローバル変数になります。

ただし、ここで大事なのは、グローバル変数は
宣言された位置より後ろで有効
だということです。

つまり、ファイルの中ならどこでも無条件に使えるわけではありません。
あくまで「宣言以降」です。

グローバル変数の特徴を表にすると、こうなります。

項目内容
宣言場所関数の外
有効範囲宣言以降のファイル内
外から見えるか同じソースファイル内では見える

スコープを読み解こう

では、ローカル変数とグローバル変数のスコープを確認するために、次のプログラムを見てみましょう。

ファイル名:13_17_1.c

#include <stdio.h>
void show_message(void);
void print_number(int value);

int g_score = 80;

int main(void)
{
    int count = 3;

    show_message();
    print_number(50);

    for (int step = 1; step <= 3; step++) {
        int count = 100;
        /* ここで処理 */
    }

    return 0;
}

int g_bonus = 20;

void show_message(void)
{
    int local_a = 10;
    /* ここで処理 */
}

void print_number(int value)
{
    int local_b = 30;
    /* ここで処理 */
}

このプログラムには、

  • グローバル変数
  • main 関数のローカル変数
  • for 文のローカル変数
  • 別の関数のローカル変数
  • 仮引数

が入っています。
スコープの違いを確認するには、とてもよい例です。

g_score のスコープ

まず、プログラムの上のほうにある

int g_score = 80;

はグローバル変数です。

この g_score は、宣言位置より後ろにある

  • main 関数
  • show_message 関数
  • print_number 関数

の中から使えます。

つまり、g_score はかなり広い範囲から見える変数です。

ここでのポイントは、関数をまたいで使えることです。
ローカル変数とは、この点が大きく違います。

main 関数の count のスコープ

次に、main 関数の中にある

int count = 3;

です。

これはローカル変数なので、main 関数の中だけで有効です。

show_message 関数や print_number 関数では使えません。
また、main 関数の中でも、この宣言より前では使えません。

つまり、この count の有効範囲は main 関数のブロック内に限定されています。

for 文の step のスコープ

for 文の中では、

for (int step = 1; step <= 3; step++)

と書かれています。

この step は、for 文の中で宣言されたローカル変数です。
そのため、for 文のブロック内でだけ有効です。

ループの中では使えますが、ループが終わったあとでは使えません。

たとえば、for 文の後ろで step を表示しようとすると、コンパイルエラーになります。

このことからも、ローカル変数は「宣言した場所の近くの狭い範囲だけで使う変数」だと分かります。

for ブロック内の count のスコープ

for 文の中にはさらに、

int count = 100;

があります。

これは main 関数の外側にある count と同じ名前ですが、別のローカル変数です。

有効範囲を整理すると、次のようになります。

変数名宣言場所有効範囲
count main 内main 関数内main 関数のブロック内
count for 内for ブロック内for ブロック内

つまり、同じ名前でも、スコープが違えば別物です。

そして、for ブロックの中で count と書いた場合には、より内側で宣言された count が優先されます。

show_message 関数の local_a のスコープ

次は show_message 関数の中です。

int local_a = 10;

この local_a は、show_message 関数のローカル変数です。
したがって、show_message 関数の中でだけ使えます

main 関数や print_number 関数からは見えません。

ローカル変数は、その関数専用の作業用変数として使うことが多いので、local_a もその典型だといえます。

print_number 関数の仮引数 value のスコープ

print_number 関数には仮引数があります。

void print_number(int value)

この value も、print_number 関数の中でだけ有効です。
仮引数もローカル変数の一種として考えると理解しやすいです。

つまり、value は print_number 関数の中だけで使える変数です。

print_number 関数の local_b のスコープ

さらに、print_number 関数の中には

int local_b = 30;

があります。

これもローカル変数なので、print_number 関数の中でだけ有効です。
show_message 関数や main 関数からは使えません。

g_bonus のスコープ

次に注目したいのは、

int g_bonus = 20;

です。

これもグローバル変数ですが、g_score と違って main 関数の後に宣言されています。

そのため、g_bonus は

  • show_message 関数
  • print_number 関数

では使えますが、main 関数の中ではこのままの順序では使えません。

ここで大事なのは、グローバル変数でも、宣言より前では見えないということです。

これはとても大切なルールなので、しっかり意識しておきたいところです。

このプログラムのスコープを表で整理するとこうなる

ここまでの内容をまとめると、次のようになります。

変数名種類宣言場所有効範囲
g_scoreグローバル変数関数の外宣言以降のファイル内
g_bonusグローバル変数関数の外宣言以降のファイル内
count main 内ローカル変数main 関数内main 関数内
stepローカル変数for 文内for ブロック内
count for 内ローカル変数for ブロック内for ブロック内
local_aローカル変数show_message 関数内show_message 関数内
value仮引数 ローカル変数print_number の引数print_number 関数内
local_bローカル変数print_number 関数内print_number 関数内

この表を見ると、変数は「どこで宣言したか」によって使える範囲が決まることがよく分かります。

名前が重複した場合はローカル変数が優先される

C言語では、グローバル変数とローカル変数、あるいは外側のローカル変数と内側のローカル変数で、同じ名前が使われることがあります。

その場合は、より内側のスコープで宣言された変数が優先されます。

たとえば、次のような例です。

#include <stdio.h>

int value = 100;   // グローバル変数

int main(void)
{
    int value = 50;   // ローカル変数

    printf("%d\n", value);

    return 0;
}

この printf で表示されるのは 50 です。
main 関数の中では、グローバル変数 value よりも、ローカル変数 value のほうが優先されるからです。

ただし名前の重複は避けたほうがよい

文法上は、スコープが違えば同じ名前を使えることがあります。
でも、実際のプログラムではこれはあまりおすすめできません。

理由は、読み手が混乱しやすいからです。

問題点内容
どの変数を使っているか分かりにくい同じ名前が複数あるため
バグの原因になりやすい思っていた変数と別の変数が使われることがある
保守しにくい後からコードを読んだときに理解しにくい

そのため、学習用としては役立ちますが、実際には名前の重複はできるだけ避けるのがよいです。

スコープの違いを図で整理

スコープの違いは、図で見るとかなり分かりやすくなります。

この図では、グローバル変数が関数の外に置かれ、複数の関数から見える様子が示されます。
一方、main 関数の count、for 文の step、for ブロック内の count、show_message 関数の local_a、print_number 関数の value と local_b は、それぞれのブロック内だけに閉じたローカル変数として描かれます。

この図を見ると、変数ごとに見える範囲が違うことが直感的に理解しやすくなります。

スコープを意識するとコードが読みやすくなる

変数のスコープを意識すると、コードの読み方がかなり変わってきます。

たとえば、ある変数を見つけたときに、

  • これは関数の中だけで使う変数かな
  • これは複数の関数で共有している変数かな
  • この名前は外側の変数と重なっていないかな

と考えられるようになります。

こうした見方ができるようになると、長いプログラムでも整理しやすくなります。

学習のコツ

ローカル変数とグローバル変数のスコープを理解するときは、まずその変数がどこで宣言されているかに注目するのがいちばん大切です。

確認のしかたを表にすると、次のようになります。

見る順番確認すること
1変数は関数の中か外か
2どの { } の中で宣言されているか
3その宣言より後ろで使っているか
4同じ名前の変数が内側にないか

この見方を習慣にすると、スコープの理解がぐっと深まります。

また、学習中は同じ名前の変数が重なる例を見て仕組みを理解するのはとてもよいですが、実際にコードを書くときは、混乱を避けるためにできるだけ分かりやすい別の名前を付けるのがおすすめです。