★ファイル分割(スコープ2)★


グローバル変数を分割した他のファイルで使用するとどうなるのでしょうか?

プログラムを作って試してみましょう。


  main.h

  main.cpp

  sub.h

  sub.cpp

↑の4つのファイルをプロジェクトに追加してください。


<sample program 164-01>


/* main.h */

#pragma once

#include "sub.h"


/* main.cpp */

#include "main.h"

int value;

int main(void)
{
    value = 24;

    ShowValue();

    return 0;
}


/* sub.h */

#pragma once

void ShowValue(void);


/* sub.cpp */

#include "sub.h"

#include <stdio.h>

void ShowValue(void)
{
    printf("value = %d\n", value);
}


main.cpp でグローバル変数valueを宣言しています。

main関数内で「24」を代入しました。

sub.cppにはShowValue関数を作成し、グローバル変数valueの中身を表示しようとしています。

ShowValue関数はmain関数から呼び出されるよう、それぞれのヘッダファイルを準備しました。

しかし、sub.cppをコンパイルするとエラーになります。

  error C2065: 'value': 定義されていない識別子です。

このままでは sub.cpp から変数valueは使えないということです。


基本的に、他のファイルと共有したいものはヘッダファイルに書きます。

使いたいファイルからヘッダファイルをインクルードすることで、色々なものが使えるようになるのです。

今回はcppファイルで変数を宣言しているので、ダメなのかもしれません。

変数の宣言をヘッダファイルに移し、sub.h から main.h をインクルードしてみましょう。

<sample program 164-02>


/* main.h */

#pragma once

int value;

#include "sub.h"


/* main.cpp */

#include "main.h"

int main(void)
{
    value = 24;

    ShowValue();

    return 0;
}


/* sub.h */

#pragma once

#include "main.h"

void ShowValue(void);


/* sub.cpp */

#include "sub.h"

#include <stdio.h>

void ShowValue(void)
{
    printf("value = %d\n", value);
}


それぞれのcppファイルをコンパイルしてみます。

※ビルドではなく、コンパイルです。

どちらもエラーが無くなったことを確認してください。

エラーが無いことが確認出来たらビルドしてください。

するとビルドエラーが表示されるはずです。

  sub.obj : error LNK2005: "int value" (?value@@3HA) は既に main.obj で定義されています。

sub.obj とか main.obj というのはこれまで説明したことのないファイルです。

とりあえず、コンパイル後に出来上がるファイルだと思っておいてください。

2つのファイルをコンパイル後にこれらをくっつけるリンクという作業を行っています。

  error LNK2005

というのは、リンクの際に出たエラーということです。

内容としては、変数valueがすでに他のファイルで宣言されているという内容です。

この状況を説明するには、インクルードされたヘッダを展開しなければなりません。


main.cpp と sub.cpp のヘッダファイル展開後の状況を書いてみます。

インクルードガードがありますので、それも考慮して書きます。



/* main.cpp */

int value;

void ShowValue(void);

int main(void)
{
    value = 24;

    ShowValue();

    return 0;
}


/* sub.cpp */

int value;

void ShowValue(void);

#include <stdio.h>

void ShowValue(void)
{
    printf("value = %d\n", value);
}


順を追って展開していけば↑のような状態になります。

main.cpp は変数valueもあり、ShowValue関数のプロトタイプ宣言もありますので、この状態でコンパイルしてもエラーは出ません。

※コンパイル時には、関数の本体が無くてもプロトタイプ宣言があれば大丈夫です。

※他のファイルとリンクした時に、本体があれば良いのです。

sub.cpp にも変数valueがあり、それを表示する関数も問題ありませんので、コンパイルエラーにはなりません。

しかし、ビルドした時にリンクしようとしたところ、同じ名前の変数valueが2つあることになります。

名前が同じだからと言って、自動的に統合されないのです。

同じレベル(スコープ)に全く同じ変数を2つ作ることは出来ないのでエラーになっているのです。


では、対処法はどうすれば良いのでしょう・・・

関数を思い出してください。

関数の本体はcppファイルにあり、プロトタイプ宣言という最低限の情報だけがヘッダファイルにあります。

最低限の情報とは、

  戻り値、関数名、引数の型と数

です。

これさえあればコンパイル出来ます。

実は、グローバル変数も同じで本体はcppファイルに作り、最低限の情報をヘッダファイルに書くことで共用出来るようになるのです。

では、プログラムを書いてみます。

<sample program 164-03>


/* main.h */

#pragma once

extern int value;

#include "sub.h"


/* main.cpp */

#include "main.h"

int value;

int main(void)
{
    value = 24;

    ShowValue();

    return 0;
}


/* sub.h */

#pragma once

#include "main.h"

void ShowValue(void);


/* sub.cpp */

#include "sub.h"

#include <stdio.h>

void ShowValue(void)
{
    printf("value = %d\n", value);
}


変数valueの本体は main.cpp に戻しました。

ヘッダファイルには、先頭に extern というキーワードを付けた宣言を書いています。

これが関数のプロトタイプ宣言に当たるものです。

これならビルドもエラーにならず、実行することが出来ます。

<実行結果>

value = 24
続行するには何かキーを押してください・・・

これで複数のファイルをまたいで使える変数が使えるようになりました。

しかし、前回も書いた通りグローバル変数は危険な面があり、極力使わないようにしたいです。

上のプログラムもShowValue関数に引数として渡せば良い話です。

わざわざ危険を冒す必要は無いですが、プログラムによっては使わないといけない場合や使ったほうが速度が上がることがあります。

個人で規模の小さいプログラムを作る場合は、ほぼ不要だと思います。


次へ

戻る

目次へ