関数の外に置かれたstatic変数

Copyright(C) 14May2003 coskx

マルチソースファイルでプログラミング開発をする時,あるソースファイル中で使っていた関数の外に置かれた共通変数名を,別のソースファイル中で使うと,思わぬエラーとなる。しかし,多くのファイルに分かれた巨大プログラムでは,変数名をぶつけてしまう事故も起きる。この事故を回避するのがstatic変数である。

関数の内部に宣言されたstatic変数は,その関数が最初に呼ばれた時点で初期化(宣言のときの代入)され,2回目以降呼び出されたときには前回関数の作業が終った時の変数の値を保っているという変数であった。(List1参照)

List 1 関数内部のstatic変数の例

#include <stdio.h>

void function(void) /*引数無し,返す値無しの関数function*/
{
    static int watchme=7;
    printf("watchme=%d\n",watchme);
    watchme++;
}

int main()
{
    function();
    function();
    function();
    function();
    function();
    return 0;
}

実行結果
watchme=7
watchme=8
watchme=9
watchme=10
watchme=11

ソースファイルが複数に分かれている,マルチソースファイルの場合には関数の外にstatic宣言をされた変数があることがある。
ファイル「datamodule.c」中の関数の外にstatic宣言をされた変数があるが,これはこのソースファイル「datamodule.c」中の関数からのみ参照・変更できるが,他ファイル中の関数(ここではdatamain.c中のmain())からは参照すら出来ないことを表している。
このようにすることで,他のファイルからは参照も変更も出来ないが,自分のファイル中のすべての関数から参照も変更も出来,安心して使えるようになる。
もともと「static」は静的の意味であるが,ここでは「local」の意味と解釈した方がよい。

List 2 関数の外部にあるstatic変数の例
(1)datamain.c

#include <stdio.h>
#include "datamodule.h"

person_t people[10]={
    {"ジョーンズ博士",48},{"小坂先生",38},{"ハリーポッター",32},{"スカイウォーカ",20}
};

int main()
{
    int number,i;
    person_t person;
    initializeModule();
    registerPerson(people[3]);
    registerPerson(people[2]);
    registerPerson(people[1]);
    registerPerson(people[0]);
    number=getNumberPersons();
    for (i=0;i<number;i++) {
        person=getPerson(i);
        printf("No.%d %-20s (%d)\n",i,person.name,person.age);
    }
    return 0;
}

(2)datamodule.h

typedef struct {
    char name[32];
    int age;
} person_t;

void initializeModule(void);     /*このモジュールの初期化*/
void registerPerson(person_t p); /*人物データの登録*/
int getNumberPersons(void);      /*登録人数の取得*/
person_t getPerson(int number);  /*番号を指定して人物データの取得*/

(3)datamodule.c

#include "datamodule.h"

static person_t people[100];/*登録場所MAX100*/
static int numberPersons=0; /*現在登録されている人数*/
static const person_t nobody={"",-1};

void initializeModule(void)     /*このモジュールの初期化*/
{
    numberPersons=0;
}

void registerPerson(person_t p) /*人物データの登録*/
{
    people[numberPersons]=p;
    numberPersons++;
}

int getNumberPersons(void)      /*登録人数の取得*/
{
    return numberPersons;
}

person_t getPerson(int number)  /*番号を指定して人物データの取得*/
{
    person_t ret;
    if (0<=number&&number<numberPersons) {
        ret=people[number];
    } else {
        ret=nobody;
    }
    return ret;
}

実行結果
No.0 スカイウォーカ       (20)
No.1 ハリーポッター       (32)
No.2 小坂先生             (38)
No.3 ジョーンズ博士       (48)


一般的には次のようなファイル構成となる。
「〜.h」には,元になる「〜.c」に定義されている関数のプロトタイプ宣言が書いてあり,関数が使われる「〜〜.c」にインクルードされるように使われる。なお,例えばsub3.cに定義されている関数がsub2.cで使われるなら,sub2.cにsub3.hをインクルードする必要がある。
sub1.c,sub2.c,sub3.cそれぞれにstaticのついた変数があるが,これらは定義されたファイル内でのみ有効な変数である。また,「static int var1;」があるが,この3つの変数は別々のものであり,定義されたファイル中のみ有効である。

main()を含むソース sub1.c sub2.c sub3.c

#include <...>
#include "sub1.h"
#include "sub2.h"
#include "sub3.h"

int funcm1(...)
{
    :
}

int funcm2(....)
{
    :
}

    :

int main()
{
    :
}

#include <...>
#include "sub1.h"

static int var1;
static double var2;

int func11(...)
{
    :
}

int func12(....)
{
    :
}

    :

#include <...>
#include "sub2.h"

static char buff[128];
static int var1;

int func21(...)
{
    :
}

int func22(....)
{
    :
}


    :
#include <...>
#include "sub3.h"

static int var1;

int func31(...)
{
    :
}

int func32(....)
{
    :
}


    :
  sub1.h sub2.h sub3.h
  int func11(...);
int func12(...);
    :
int func21(...);
int func22(...);
    :
int func31(...);
int func32(...);
    :