ショートカット
ファシリテーター × あり方
コーディングの向こう側
Hello, ANOTHER world!
オブジェクト指向のはなし
プログラミングのはなし
C言語実力診断クイズ
eSkillBooks
プログラミングのはなし

超重要なファイルスコープ

分割コンパイルをするとき、グローバルスコープの変数をいかにして少なくするかというのが重要です。ファイルスコープにする必要がある変数とは、「モジュール内の複数の関数からアクセスされるがモジュール外からはアクセスされない変数」です。グローバル変数=「モジュール外からもアクセスされる変数」の扱い方についてはあとで説明します。

例えば次のようなプログラムがあったとします(実用的なプログラムではありません)。

SAMPLE
CODE
ファイル名:main.cpp
#include "sub.h"

void main()
{
    Open( "filename" );
    Close();
}
ファイル名:sub.h
#ifndef INCLUDED_SUB_H
#define INCLUDED_SUB_H

void Open( const char *pcFileName );
void Close();

#endif // #ifndef INCLUDED_SUB_H
ファイル名:sub.cpp
#include <assert.h>
#define ASSERT(C) assert(C)

#include "sub.h"

static bool fs_bOpen = false;

void Open( const char *pcFileName )
{
    ASSERT( fs_bOpen == false );
    ...
    fs_bOpen = true;
}

void Close()
{
    ASSERT( fs_bOpen == true );
    fs_bOpen = false;
    ...
}

モジュール main はモジュール sub の関数 Open() と Close() を呼んでいます。Open() はすでにオープンされている場合をエラーとするために ASSERT し、Close() はすでにオープンされていない場合をエラーとするために ASSERT しています(ASSERT については別のトピックで詳しく説明しています)。

念のために説明しておきますが、sub.h で定義しているマクロ INCLUDED_SUB_H はインクルードの重複を可能にするためのものです。1回のコンパイルで sub.h が何度インクルードされてもかまわないようにしています。これはインクルードファイルを記述するときに必ずやらなければならない基本中の基本なので、知らなかった人は覚えて下さい。

では本題に入ります。

sub.cpp で fs_bOpen という変数がありますね。これがファイルスコープ変数です。モジュール内の複数の関数からアクセスされるため、関数の外側で定義されています。そしてキーワード static をつけることによって他のモジュールから見えないようにしています(static をつけないとグローバル、つまり他のモジュールからも見えるようになります)。

「他のモジュールから見えない」ということが非常に重要です。もし fs_bOpen をグローバルにしてしまったら、いつその値が変更されてしまうかわかりません。ファイルスコープにすることによって、その変数の値が予期できないタイミングで変更されないことが保証されるのです。つまり、変数 fs_bOpen は完全にモジュール sub の支配下であり、モジュール内の関数を修正するときにはそのモジュールにだけ注意を注げばよいことになります。これがファイルスコープにする理由の本質です。

情報系の仕事をしている人なら「情報隠蔽」という言葉を聞いたことがあるでしょう。これはまさに「コーディングするときに同時に気を配る必要がある範囲をいかにして狭めるか」という問いへの答えです。そしてモジュール分割による設計における情報隠蔽の手段がファイルスコープ変数なのです。

さいごに注意しなければいけないことをひとつ。ファイルスコープ変数は静的です。したがってスレッドセーフではありません。スレッドを用いたプログラムでファイルスコープ変数にアクセスするときは、その部分をクリティカルセクションにしなければなりません。

(「プログラミングのはなし」は1998年1月から1999年1月にかけて作成されたコンテンツです。)