C言語入門|危険と隣り合わせのグローバル変数

引数や戻り値のルールを学んでいると、こんな気持ちになることがあります。

「引数とか戻り値とか、正直ちょっと面倒じゃないですか?」
「main 関数で用意した変数を、どこからでも使えたら楽なのに……」

うん、その感覚、とても自然です。
実はC言語には、その願いを叶える仕組み がちゃんと用意されています。

それが グローバル変数 です。

ただし、この仕組みは
便利さと引き換えに、かなり危険な性質 も持っています。

グローバル変数とは何か

これまで変数は、main 関数や add 関数など、
関数の中 で宣言してきました。

ところがC言語では、
すべての関数の外 に変数を宣言することができます。

プロジェクト名:8-13-1 ソースファイル名: sample8-13-1.c

#include <stdio.h>

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

void showValue(void)
{
    printf("現在の値は %d です\n", baseValue);
}

int main(void)
{
    baseValue = 150;
    showValue();
    return 0;
}

このように宣言された変数を グローバル変数 と呼びます。

特徴をまとめると、次のようになります。

項目内容
宣言場所関数の外
有効範囲宣言された行以降のすべての関数
アクセスどの関数からも読み書き可能

「おお、全部の関数から使える!便利!」

……そう思いますよね。
でも、ここで一度立ち止まりましょう。

便利さの裏に潜むデメリット

「すべての関数で同じ変数を使える」
これは一見すると、とても魅力的です。

しかし、この性質には次のような 大きな欠点 があります。

問題点内容
名前の衝突別の場所で同名変数を宣言できない。
予期せぬ変更どの関数でも値を書き換えられてしまう。
デバッグ困難どこで値が変わったのか追いにくい。

関数が3つや4つ程度なら、まだ把握できるかもしれません。
ですが、関数が増えてくると状況は一変します。

「この値、いつ変わったんだ?」
「え、ここで書き換えられてるの?」

こうした問題が積み重なり、
グローバル変数が致命的な不具合の原因になる ことは、
昔からよく知られています。

なぜグローバル変数は嫌われがちなのか

実務の現場では、

  • グローバル変数は原則禁止
  • 使用する場合は厳格なルール付き

というプロジェクトも珍しくありません。

中には、

  • グローバル変数を使えない言語
  • 使うと警告が出る言語

も存在します。

それほどまでに、
グローバル変数は扱いが難しい存在 なのです。

とはいえ、完全な悪者というわけではありません。

グローバル変数に頼る前に考えること

グローバル変数を使いたくなったら、
まず次の問いを自分に投げかけてみてください。

引数と戻り値で解決できないだろうか?

多くの場合、
引数と戻り値を使えば、きれいに解決できます。

グローバル変数は、
「どうしてもそれしか方法がないときの最終手段」
くらいに考えておくのが安全です。

変数以外にも存在するグローバルな宣言

関数の外で宣言できるのは、変数だけではありません。

次のような定義も、
関数の外に記述することができます。

宣言できるもの
列挙体enum
構造体型struct
型定義typedef
enum Element { FIRE, WATER, WIND, EARTH };

int main(void)
{
    printf("%d\n", FIRE);
    return 0;
}

このように定義すると、
FIRE や WIND をすべての関数で安全に利用できます。

これは 問題の少ないグローバル利用 の代表例です。

名前の重複と隠蔽に注意

C言語では、
ブロックの内外で同じ名前の変数を宣言すること ができます。

int age = 4;   // グローバル変数

int main(void)
{
    int age = 39;   // ローカル変数
    printf("%d\n", age);  // 39 が表示される
    return 0;
}

この場合、main 関数の中では
ローカル変数 age が優先 され、
グローバル変数 age は隠蔽されます。

この挙動を理解していないと、
思わぬ不具合につながることがあります。

安全に書くためのコーディング指針

グローバル変数を使う場合は、
次のようなルールを意識すると安全性が高まります。

ルール内容
グローバル変数を乱用しない。
変数名の先頭に g_ などの接頭辞を付ける。
ブロックのネストを深くしすぎない。
ループ変数以外で i や j を多用しない。

これだけでも、
トラブルの多くは防げます。

まとめ:便利だが常に危険と隣り合わせ

グローバル変数は、

  • 書くのは楽
  • 使うのも楽

その一方で、

  • 壊すのは一瞬
  • 直すのは大変

という性質を持っています。

まずは 引数と戻り値で設計する
それでもどうしても必要なときに、
リスクを理解したうえで使う。

これが、C言語と長く付き合うためのコツです。