6日でできる 新C言語入門|グローバル変数とローカル変数の違い

変数は、どこで作るかによって使える場所が変わる。
グローバル変数とローカル変数の違いを理解して、安全で読みやすいC言語プログラムを書こう。

C言語でプログラムを書いていると、変数をどこに宣言するかがとても重要になります。変数は、宣言した場所によって使える範囲が変わります。この使える範囲のことをスコープ、または有効範囲と呼びます。

特に大切なのが、グローバル変数とローカル変数の違いです。

グローバル変数は、関数の外で宣言する変数です。複数の関数から共通して使えるため、プログラム全体で共有したい値を管理できます。

一方、ローカル変数は、関数の中で宣言する変数です。その関数の中だけで使えるため、処理ごとの一時的な値を安全に扱えます。

この違いを理解していないと、別の関数で値が変わってしまったり、使えると思った変数が使えなかったりして、思わぬエラーやバグにつながることがあります。

この記事では、グローバル変数とローカル変数の基本、スコープの考え方、同じ名前の変数を使ったときの動き、グローバル変数を使うときの注意点、main関数の戻り値まで、サンプルプログラムを使いながらやさしく解説していきます。

変数には使える範囲がある

C言語の変数は、どこで宣言したかによって使える場所が決まります。

たとえば、関数の中で宣言した変数は、その関数の中だけで使えます。別の関数から直接使うことはできません。

void showValue(void) {
    int value = 10;

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

この場合、valueはshowValue関数の中で宣言されています。そのため、showValue関数の中では使えますが、main関数や別の関数から直接使うことはできません。

一方、関数の外で宣言した変数は、複数の関数から使えます。

int count = 0;

void addCount(void) {
    count++;
}

この場合、countは関数の外で宣言されているため、addCount関数から使えます。

このように、変数には見える範囲があります。スコープを理解すると、どの変数がどこで使えるのかを整理しやすくなります。

グローバル変数とは

グローバル変数は、関数の外で宣言する変数です。通常、ソースファイルの先頭付近に書きます。

int totalCount = 0;

このように関数の外に書かれた変数は、同じソースファイル内の複数の関数から参照したり変更したりできます。

グローバル変数は、プログラム全体で共通して使いたい値を扱うときに使えます。たとえば、ゲームのスコア、全体のカウント、共通設定などです。

ただし、どの関数からでも変更できるため、値がどこで変わったのか分かりにくくなることがあります。便利な反面、使いすぎには注意が必要です。

ローカル変数とは

ローカル変数は、関数の中で宣言する変数です。

int main(void) {
    int score = 80;

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

    return 0;
}

この場合、scoreはmain関数の中で宣言されているため、main関数の中だけで使えます。

別の関数の中でscoreを使おうとしても、その変数は見えません。別の関数で同じ名前の変数を宣言した場合でも、それは別のローカル変数として扱われます。

ローカル変数は、関数ごとに処理を独立させたいときに便利です。関数の中だけで必要な値は、基本的にローカル変数として宣言すると、プログラムの安全性が高くなります。

図:グローバル変数とローカル変数の有効範囲

この図から分かること

この図から分かるのは、グローバル変数とローカル変数では使える範囲が大きく違うということです。

グローバル変数は関数の外で宣言されるため、複数の関数から参照できます。一方、ローカル変数は関数の中で宣言され、その関数の中だけで使えます。どこに変数を置くかによって、プログラム全体への影響が変わることを意識しましょう。

グローバル変数とローカル変数を比較するプログラム

次のプログラムでは、グローバル変数とローカル変数の動きを確認します。

グローバル変数としてtotalCountを用意し、複数の関数から参照します。また、それぞれの関数の中にローカル変数を用意し、その関数の中だけで使われることを確認します。

プロジェクト/ファイル名: Lesson40_1/main.c

#include <stdio.h>

// グローバル変数
int totalCount = 5;

// プロトタイプ宣言
void addCount(void);
void showStatus(void);

int main(void) {
    int localScore = 20; // main関数のローカル変数

    printf("main関数内\n");
    printf("totalCount = %d\n", totalCount);
    printf("localScore = %d\n", localScore);
    printf("--------------------\n");

    addCount();
    showStatus();

    return 0;
}

void addCount(void) {
    int addValue = 3; // addCount関数のローカル変数

    // グローバル変数totalCountの値を増やす
    totalCount += addValue;

    printf("addCount関数内\n");
    printf("totalCount = %d\n", totalCount);
    printf("addValue = %d\n", addValue);
    printf("--------------------\n");
}

void showStatus(void) {
    int displayNumber = 100; // showStatus関数のローカル変数

    printf("showStatus関数内\n");
    printf("totalCount = %d\n", totalCount);
    printf("displayNumber = %d\n", displayNumber);
    printf("--------------------\n");
}

実行結果

main関数内
totalCount = 5
localScore = 20
--------------------
addCount関数内
totalCount = 8
addValue = 3
--------------------
showStatus関数内
totalCount = 8
displayNumber = 100
--------------------

このプログラムでは、totalCountが関数の外で宣言されています。

int totalCount = 5;

そのため、main関数、addCount関数、showStatus関数のどこからでも使えます。

一方、localScore、addValue、displayNumberは、それぞれ関数の中で宣言されています。

int localScore = 20;
int addValue = 3;
int displayNumber = 100;

これらはローカル変数なので、宣言された関数の中だけで使えます。たとえば、addValueはaddCount関数の中では使えますが、main関数やshowStatus関数から直接使うことはできません。

グローバル変数は複数の関数で共有される

実行結果を見ると、最初にmain関数で表示したtotalCountは5です。

その後、addCount関数の中で次の処理が行われます。

totalCount += addValue;

addValueは3なので、totalCountは5から8になります。

その後、showStatus関数でtotalCountを表示すると、8になっています。

これは、totalCountがグローバル変数であり、複数の関数で同じ変数を共有しているためです。

処理場所totalCountの値
main関数で最初に表示5
addCount関数で3を加算8
showStatus関数で表示8

グローバル変数は、関数をまたいで値を共有できます。便利ですが、どの関数が値を変更したのかを把握しにくくなる場合があります。

ローカル変数は宣言した関数の中だけで使える

ローカル変数は、宣言した関数の中だけで使える変数です。

今回のプログラムでは、localScoreはmain関数の中だけで使えます。

int localScore = 20;

addValueはaddCount関数の中だけで使えます。

int addValue = 3;

displayNumberはshowStatus関数の中だけで使えます。

int displayNumber = 100;

たとえば、main関数の中で addValue を表示しようとしても、addValue は addCount関数の中で宣言された変数なので使えません。

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

このように書くと、main関数の中では addValue が見つからないため、エラーになります。

ローカル変数は使える範囲が限られているため、ほかの関数へ余計な影響を与えにくいというメリットがあります。

グローバル変数とローカル変数の違い

グローバル変数とローカル変数の違いを整理すると、次のようになります。

項目グローバル変数ローカル変数
宣言する場所関数の外関数の中
使える範囲同じソースファイル内の複数の関数宣言した関数の中だけ
値の共有複数の関数で同じ値を共有できる関数ごとに独立する
使いどころ全体で共有したい設定や状態関数内だけで使う一時的な値
注意点どこで変更されたか分かりにくい関数の外から直接使えない

基本的には、必要な範囲が狭い変数はローカル変数にするのがおすすめです。どうしても複数の関数で共有したい値がある場合に、グローバル変数を検討するとよいでしょう。

同じ名前の変数がある場合の動き

C言語では、グローバル変数とローカル変数で同じ名前を使うと、関数の中ではローカル変数が優先されます。

たとえば、次のようなコードを考えます。

int value = 10;

void test(void) {
    int value = 99;

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

この場合、test関数の中で表示されるのは99です。関数の中にvalueというローカル変数があるため、グローバル変数のvalueではなく、ローカル変数のvalueが使われます。

一方、ローカル変数がない関数では、グローバル変数が使われます。

int value = 10;

void show(void) {
    printf("%d\n", value);
}

この場合、show関数の中にはvalueというローカル変数がないため、グローバル変数のvalueが表示されます。

図:同じ名前の変数があるとローカル変数が優先される

この図から分かること

この図から分かるのは、同じ名前の変数があっても、どの変数が使われるかはスコープによって決まるということです。

関数の中に同じ名前のローカル変数がある場合、その関数内ではローカル変数が優先されます。ローカル変数がない場合は、グローバル変数を参照できます。同じ名前を使うと混乱しやすいため、学習段階ではできるだけ分かりやすい名前を付けることが大切です。

同じ名前を使うときの注意点

グローバル変数とローカル変数で同じ名前を使うことはできますが、読みやすさの面では注意が必要です。

同じ名前が複数の場所にあると、コードを読む人がどちらの変数を使っているのか迷いやすくなります。

int score = 50;

void update(void) {
    int score = 80;

    printf("%d\n", score);
}

このようなコードでは、update関数の中で使われる score はローカル変数です。しかし、グローバル変数にも同じ名前があるため、慣れていないと混乱しやすくなります。

変数名は、できるだけ役割が分かる名前にするのがおすすめです。

変数名の例意味が分かりやすい理由
totalCount合計のカウントだと分かる
localScore関数内の点数だと分かる
displayNumber表示用の数値だと分かる
addValue加算する値だと分かる

特にグローバル変数は、複数の関数から見えるため、名前だけで意味が伝わるようにしておくと読みやすくなります。

グローバル変数の便利な点

グローバル変数は、複数の関数で同じ値を共有したいときに便利です。

たとえば、プログラム全体で共通して使うカウント値や設定値を扱う場合、グローバル変数にしておくと、複数の関数から参照できます。

int totalCount = 0;

void countUp(void) {
    totalCount++;
}

void showCount(void) {
    printf("%d\n", totalCount);
}

このように、countUp関数で値を増やし、showCount関数で同じ値を表示できます。

グローバル変数が向いている場面には、次のようなものがあります。

場面
プログラム全体で共有する状態全体のカウント、実行状態
複数の関数で共通して使う設定表示モード、共通の基準値
小さな学習用プログラム動きを確認するための共有変数

ただし、実用的なプログラムでは、グローバル変数を増やしすぎると管理が難しくなります。

グローバル変数を使いすぎると起こりやすい問題

グローバル変数は便利ですが、どの関数からでも変更できるため、次のような問題が起こりやすくなります。

問題内容
値がどこで変わったか分かりにくい複数の関数が変更できるため追いかけにくい
関数の独立性が下がる関数が外部の変数に依存しやすくなる
テストしにくくなる関数単体の動作確認が難しくなる
名前の衝突が起こりやすい広い範囲で見えるため名前に注意が必要

特にプログラムが大きくなると、グローバル変数の値がいつ、どこで変わったのか分からなくなることがあります。

そのため、基本的にはローカル変数を中心に使い、必要な値は引数や戻り値で関数に渡すようにすると、プログラムが安全で読みやすくなります。

図:グローバル変数は便利だが変更元が増えると追いにくい

この図から分かること

この図から分かるのは、グローバル変数は複数の関数から共有できる一方で、変更元が増えると管理が難しくなるということです。

グローバル変数は便利ですが、どの関数が値を変更したのかを追いかけにくくなる場合があります。安全に使うためには、本当に共有が必要な値だけに絞ることが大切です。

ローカル変数を中心に使うと安全に書きやすい

ローカル変数は、宣言した関数の中だけで使えるため、影響範囲が狭くなります。

たとえば、次のように関数の中で一時的に使う変数は、ローカル変数にするのが自然です。

void showTotal(int price, int count) {
    int total;

    total = price * count;

    printf("合計金額は %d 円です。\n", total);
}

この場合、totalはshowTotal関数の中だけで必要な変数です。ほかの関数から使う必要はありません。

ローカル変数を中心に使うと、次のようなメリットがあります。

メリット内容
影響範囲が分かりやすいその関数の中だけを見ればよい
バグを追いやすい値がどこで変わるか限定される
関数を再利用しやすい外部の変数に依存しにくい
名前を使い分けやすい関数ごとに同じ名前でも独立して扱える

関数の中だけで使う値は、できるだけローカル変数として宣言することを意識しましょう。

値を共有したいときは引数と戻り値も考える

複数の関数で値をやり取りしたい場合、すぐにグローバル変数を使うのではなく、引数と戻り値を使う方法も考えましょう。

たとえば、次のように、関数へ値を渡して結果を受け取ることができます。

int addTax(int price) {
    return price + price * 10 / 100;
}

呼び出し側では、次のように戻り値を受け取ります。

int result;

result = addTax(1000);

このようにすれば、グローバル変数を使わなくても、関数同士で値をやり取りできます。

方法特徴
グローバル変数複数の関数で同じ値を共有できるが、変更元が増えやすい
引数関数へ必要な値を渡せる
戻り値関数の結果を呼び出し元へ返せる
ローカル変数関数内だけで安全に使える

基本はローカル変数、必要な値は引数で渡す、結果は戻り値で返す。この考え方を持つと、関数の役割が分かりやすくなります。

main関数の戻り値の意味

C言語のmain関数では、最後にreturn 0;を書くことがよくあります。

int main(void) {
    return 0;
}

このreturn 0;は、プログラムが正常に終了したことをOSへ知らせるためのものです。

一般的には、0は正常終了を表します。0以外の値は、何らかの異常終了やエラーを表す値として使われることがあります。

戻り値一般的な意味
0正常終了
1などの0以外異常終了やエラー

たとえば、何かの入力に失敗した場合にreturn 1;を使うことで、プログラムが正常に終わらなかったことを呼び出し元へ伝えることがあります。

学習用の基本プログラムでは、最後にreturn 0;を書いて、正常終了を表す形に慣れておくとよいでしょう。

スコープを理解すると変数の使い方が整理できる

グローバル変数とローカル変数の違いは、変数の宣言場所と有効範囲の違いです。

グローバル変数は関数の外で宣言し、複数の関数から使えます。値を共有したいときには便利ですが、どこで値が変更されたのか分かりにくくなることがあります。

ローカル変数は関数の中で宣言し、その関数の中だけで使えます。影響範囲が狭いため、関数ごとに安全に値を扱いやすくなります。

プログラムを書くときは、まずローカル変数で済むかを考えましょう。複数の関数で値をやり取りしたい場合は、引数や戻り値を使う方法もあります。どうしてもプログラム全体で共有したい値がある場合に、グローバル変数を使うと整理しやすくなります。

スコープを意識できるようになると、変数の見える範囲や影響範囲が分かりやすくなり、バグを防ぎやすいプログラムを書けるようになります。