C言語のスコープ(変数や関数などの有効範囲)には、以下の3つのスコープがあります。
- グローバルスコープ:どこからでもアクセスできる
- ファイルスコープ:モジュール内のみアクセスできる
- ローカルスコープ(ブロックスコープ):ブロック内のみアクセスできる
ファイルスコープにする必要がある変数とは、「モジュール内の複数の関数からアクセスされるが、モジュール外からはアクセスされない変数」です。
例えば、次のようなC言語のプログラムがあったとします。ドキュメントファイルを扱うためのモジュールです(細かい処理は省略しています)。
#ifndef document_h
#define document_h
void OpenDocument(const char *pDocumentName);
void CloseDocument(void);
#endif /* document_h */
#include <assert.h>
#include "document.h"
typedef enum {
Closed,
Open
} DocumentOpenStatus;
static DocumentOpenStatus openStatus = Closed;
void OpenDocument(const char *pDocumentName) {
assert(openStatus == Closed);
/* ドキュメントを開く処理(省略) */
openStatus = Open;
}
void CloseDocument(void) {
assert(openStatus == Open);
/* ドキュメントを閉じる処理(省略) */
openStatus = Closed;
}
OpenDocument()
では、すでにドキュメントが開いている状態での呼び出しを禁止するためにassert()
を使っています。同様に、CloseDocument()
ではドキュメントが開いていない状態での呼び出しを禁止しています。
assert()
については別のトピックで詳しく説明します。
上記のモジュールは、main()
から次のように呼びだされます。
#include <stdlib.h>
#include "document.h"
int main(void) {
OpenDocument("document-name");
CloseDocument();
return EXIT_SUCCESS;
}
では本題に入りましょう。
「document.c」には、openStatus
という変数がありますね。これがファイルスコープ変数です。モジュール内の複数の関数からアクセスされるため、関数の外側で定義されています。
そして、キーワードstatic
を付けることによって、ほかのモジュールからは見えないようにしています。(static
を付けないとグローバルスコープ、つまりほかのモジュールからも見える変数になります。)
もしopenStatus
がグローバルだったら、いつその値が変更されてしまうか分かりません。ファイルスコープにすることによって、変数の値が予期せぬタイミングで変更されないようにしているのです。
また、この変数はモジュール「document.c」の支配下にあります。
ファイルスコープ変数にしておけば、変数の有効範囲はモジュール内だけになります。そのため「document.c」を修正するときには、変数がモジュール内でどのように振る舞うかにだけ注意を払えばよいのです。
情報系の仕事をしている人なら「情報隠蔽」という言葉を聞いたことがあるでしょう。これはまさに「コーディングするときに同時に気を配る必要がある範囲をいかにして狭めるか」という問いへの答えです。