グローバル変数

提供: MonoBook
移動: 案内検索

グローバル変数英語: global variable)とは、プログラミングにおいて全てのスコープからアクセスできる変数のことである。 日本語では大域変数などと呼ばれるが、相手がプログラマーであればプログラミング言語を問わずほぼ「グローバル変数」で意味は通じる。 対する語はローカル変数である。

スコープ」の項目も参照。

概要[編集]

欠点[編集]

一般にグローバル変数は、その非局在的な性質ゆえに好ましくない実践だと考えられている。すなわちグローバル変数は潜在的にどこかで変更される可能性があり、またプログラムの一部はそれに依存してしまう恐れがあるからである。グローバル変数はそれゆえ相互依存を生み出す無限の可能性を持っており、相互依存が高まることは複雑性を増大することにつながる。

また、純粋なグローバル変数ではないが、オブジェクト指向におけるクラス変数などは、すべてのスコープからはアクセスできないが関数の動作に影響を与える準グローバル変数と言える。これらが連鎖することで「クラスの中で使われてるクラスの中で使われているクラスを完全に把握していないとどういう動作になるかわからない」という、単純なグローバル変数よりも恐ろしい状況に陥る。この悪夢を副作用という。

ダイナミックリンクライブラリのような多くのシステムは他のモジュールにあるグローバル変数の参照を直接サポートしないため、グローバル変数が多用されているコードを分割して再利用ライブラリにすることは非常に困難である。また、グローバル変数が他のローカルまたはオブジェクトスコープ変数として代用するには危険な名前を作るために起こる名前問題を導いてしまうことがある。グローバル変数と同じ名前のローカル変数は、一般にそのローカル変数のスコープにおいてグローバル変数を隠匿する。そのため、さらにコードの理解を困難にする。グローバル変数への値の書き込みは理解と予測を難しくする副作用を生み出す。

グローバルなどの利用はユニットテストを目的とするコード分割をより難しくする。それゆえ、それらは直接にコードの質の低下を助長する。

利点[編集]

しかしグローバル変数が適する状況もある。たとえば様々な関数を通して継続的によく使われる変数を引数として渡すことを避けるために使用できる。また、並列実行するスレッドプロセスのように、呼び出し側と被呼び出し側の関係を持たないコード同士でセクション間の情報を受け渡すためにも広く使われる。

また、各々のファイルが暗黙の名前空間(無名名前空間)を定義するような言語では、グローバルな名前空間を持つ言語に見られる問題の多くが解消されているが、一部の問題は適切なカプセル化を行わなければ解決できない。ミューテックスのような適切なロック無しにグローバル変数を使用しているコードは、保護メモリ内の読み出し専用値以外はスレッドセーフではない。

実装例[編集]

C++におけるグローバル変数の例:

#include <iostream>
 
int global = 3 ; // これはグローバル変数。
 
void ChangeGlobal ()
{
    global = 5 ; // 関数でグローバル変数を書き換え。
}
 
int main (int argc, char *argv [])
{
    std::cout << global << std::endl ; // もう一つの関数でグローバル変数を参照。
    ChangeGlobal () ;
    std::cout << global << std::endl ;
    return 0 ;
}

この変数はグローバルなので、main以外の全ての関数にそれを引数として渡す必要がない。グローバル変数はプログラム内で、全ての関数に属する。

この出力結果は、以下のようになる:

3
5

グローバル変数の問題点[編集]

グローバル変数を使用すると、ソフトウエアソースコードを理解することが困難になる。グローバル変数の値はプログラムのどこからでも変更できるため、グローバル変数がどのように使用されているか理解するためには、プログラム全体を把握しなければならないからである。

以下にグローバル変数により難解になる例を示す:

int a, b, c ; // グローバル変数宣言
 
int main (int argc, char *argv [])
{
    FunctionA () ; // グローバル変数cを書き換える
    FunctionB () ; // グローバル変数cを元にグローバル変数aを書き換える
    FunctionC () ; // グローバル変数a, cを元にグローバル変数bを書き換える
    return 0 ;
}

ローカル変数で書きなおし[編集]

上記をローカル変数を用いて書きなおした例:

int main (int argc, chra *arg [])
{
    int c = FunctionA () ; // ローカル変数cを書き換える
    int a = FunctionB (c) ; // ローカル変数cを元にローカル変数aを書き換える
    int b = FunctionC (a, c) ; // ローカル変数a, cを元にローカル変数bを書き換える
    return 0 ;
}

ローカル変数を用いた例ではどの変数が、どの関数呼び出しに作用し、どの関数呼び出しの結果を保持しているかは一目瞭然である。 対して、グローバル変数を使用した例では、関数の呼び出し側だけを見てどの関数がどの変数に影響を与えるかを判断することはできない。

また、ローカル変数を用いた例では関数の呼び出し順序を入れ替えることは引数と戻り値だけ意識すれば良く容易であるのに対して、グローバル変数を用いた例ではグローバル変数の作用を全て把握する必要があるため困難である。特にこの困難度合いは、グローバル変数の数とグローバル変数を操作する可能性のある関数の数に比例する。

関連項目[編集]

参考文献[編集]