#ifndefで重複インクルードを防ぐ方法

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
きょーちゃん
記事: 11
登録日時: 13年前

#ifndefで重複インクルードを防ぐ方法

#1

投稿記事 by きょーちゃん » 13年前

初めて質問させていただきます。C++の勉強を始めたばかりの初心者です。
現在、ソースファイルの分割について勉強中です。
試しに簡単なコードを分割してみたのですが、エラーが出てしまい困ってます。

コードはこんな感じです。

test01.cpp

コード:

#include "DxLib.h"
#include "test.h"
 
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
        ChangeWindowMode( TRUE ) ; // ウインドウモードに変更
        if( DxLib_Init() == -1 ) return -1; // DXライブラリ初期化処理 エラーが起きたら終了 
 
 
		hello();
 
 
        WaitKey() ;                     // キーの入力待ち(『WaitKey』を使用)
        DxLib_End() ;                   // DXライブラリ使用の終了処理
        return 0 ;                      // ソフトの終了
}
test02.cpp

コード:

#include "DxLib.h"
#include "test.h"


void hello()
{
	
	White   = GetColor( 255 , 255 , 255 ) ; // 白色の値を取得

        DrawString(  0,  0, "hello! DX Library!" , White);            //文字列表示
        DrawString(100,100, "こんにちは! DXライブラリ!" , White);//文字列表示
}
test.h

コード:

#ifndef _TEST_H_
#define _TEST_H_

int White;

 

void hello(void);

#endif
test01.cppがメインのコードで、
test02.cppが関数の中身、
test.hがこれらに使う宣言や定義をまとめたもの、
という風に設計したつもりです。
ヘッダファイルには#ifndef を使って重複インクルードは避けてるはずなんですが、
「1>test02.obj : error LNK2005: "int White" は既に test01.obj で定義されています。
fatal error LNK1169: 1 つ以上の複数回定義されているシンボルが見つかりました。」
というエラーが出てしまいます。
いろいろ調べたのですがどこが間違っているのかわかりません。
ご教授いただければ幸いです。

Poco
記事: 161
登録日時: 13年前

Re: #ifndefで重複インクルードを防ぐ方法

#2

投稿記事 by Poco » 13年前

・test.h
int White;の頭にexternを付けてください。

・test01.cppかtest02.cpp
int White;
を追加してください。

きょーちゃん
記事: 11
登録日時: 13年前

Re: #ifndefで重複インクルードを防ぐ方法

#3

投稿記事 by きょーちゃん » 13年前

>ぽこ さん
できました!ありがとうございますm(_ _)m

ところで、#ifndef~を付けるとそこは一度しかインクルードされないはずと思っていたのですが、
このエラーメッセージからするとtest01.cppとtest02.cppの両方でインクルードされてしまってるということですか?
なぜこれだとエラーになるのかどうも納得がいかないです・・・いったいどういう処理が行われているのでしょうか??

アバター
bitter_fox
記事: 607
登録日時: 13年前
住所: 大阪府

Re: #ifndefで重複インクルードを防ぐ方法

#4

投稿記事 by bitter_fox » 13年前

一連のプログラムにつき、同名の変数の宣言は一つのみです。

ですので、ぽこさんの仰った方法か、

ヘッダファイルでの宣言を

コード:

static int White;
にするか、

マクロを使った宣言の方法を取ってください。

コード:

//ヘッダ
#ifdef GLOBAL_VALUE
#define GLOBAL
#else
#define GLOBAL extern
#endif

// 略

GLOBAL int White;

// 略

コード:


// test2.cpp
#include "DxLib.h"
#define GLOBAL_VALUE
#include "test.h"

//略
[参考]http://www.pro.or.jp/~fuji/mybooks/cdiag/cdiag.5.4.html

[hr][修正]
#endifが抜けていたので修正しました。
[修正]
Whiteがwhiteになっていたので修正しました。

きょーちゃん
記事: 11
登録日時: 13年前

Re: #ifndefで重複インクルードを防ぐ方法

#5

投稿記事 by きょーちゃん » 13年前

>bitter_foxさん
ありがとうございます!いろいろなやり方があるのですね・・・

だらだら質問してすいません。
いろいろ考えてみたのですが、つまり重複インクルードっていうのは、
1つのファイル内で同じファイルをインクルードすることであって
(例えば、A.h と B.h が C.h をインクルードし、D.h が A.h と B.h をインクルードするというような事態)、
複数のファイルからのインクルード(今回の場合test01とtest02によるtest.hのインクルード)は重複インクルードと言わないってことでしょうか。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: #ifndefで重複インクルードを防ぐ方法

#6

投稿記事 by softya(ソフト屋) » 13年前

きょーちゃん さんが書きました:>ぽこ さん
できました!ありがとうございますm(_ _)m

ところで、#ifndef~を付けるとそこは一度しかインクルードされないはずと思っていたのですが、
このエラーメッセージからするとtest01.cppとtest02.cppの両方でインクルードされてしまってるということですか?
なぜこれだとエラーになるのかどうも納得がいかないです・・・いったいどういう処理が行われているのでしょうか??
多重インクルードを防ぐだけですので、test01.cppとtest02.cppの両方に各々一回づつはインクルードされます。
されないとヘッダの内容が見えませんからね。
例えば多重インクルードを防ぐと言うのは、test01.cpp内で2度のインクルードを防ぐ行為です。
複雑なヘッダ構成でヘッダ内でヘッダをインクルードするとうっかり起こる問題を防ぐための手段と思ってください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
Dixq (管理人)
管理人
記事: 1661
登録日時: 13年前
住所: 北海道札幌市
連絡を取る:

Re: #ifndefで重複インクルードを防ぐ方法

#7

投稿記事 by Dixq (管理人) » 13年前

> きょーちゃんさん

お~、もう分割コンパイル勉強されてるんですね^^

> つまり重複インクルードっていうのは、1つのファイル内で同じファイルをインクルードすること

そうですね。
include文を書くと、示したファイルの内容をごっそりinclude文の部分に置換したような形になるとイメージすると良いと思います。

お分かりだとは思いますが、
最初test.hをincludeした時は、_TEST_H_が定義されていないので、

#ifndef _TEST_H_

を通過して、下へ降ります。
しかし、includeした他のヘッダ内で、更にtest.hをincludeした場合は、既に_TEST_H_が定義されているので、
何も処理せずにendifまで飛びます。
後はtest.hのincludeを含んだどのヘッダファイルをincludeしようが同じ動きになり、重複定義を防止できます。

なお、CではなくC++であれば、なるべく変数を隠蔽化しようという設計になるかと思います。
現在はソースが少ないので、Whiteがどこで変更されようとも一目瞭然化もしれませんが、
ファイル数が数千、ステップ数が数十万なんて単位のプロジェクトになると、
あちこちで宣言した変数がどこででも変更可能では、いったい変数がどのタイミングでどう変わるのかわけがわからなくなってしまいます。
ということで、ある程度の処理区分ごとに変数を隠蔽しようって話になります。

今回の例で言えば、

test01.cpp

コード:

#include "DxLib.h"
#include "test.h"
 
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
        ChangeWindowMode( TRUE ) ; // ウインドウモードに変更
        if( DxLib_Init() == -1 ) return -1; // DXライブラリ初期化処理 エラーが起きたら終了 
  
        hello();
  
        WaitKey() ;                     // キーの入力待ち(『WaitKey』を使用)
        DxLib_End() ;                   // DXライブラリ使用の終了処理
        return 0 ;                      // ソフトの終了
}
test.h

コード:

#ifndef _TEST_H_
#define _TEST_H_

void hello(void);
 
#endif
test02.cpp

コード:

#include "DxLib.h"
#include "test.h"

static int White;

void hello()
{    
        White   = GetColor( 255 , 255 , 255 ) ; // 白色の値を取得
 
        DrawString(  0,  0, "hello! DX Library!" , White);            //文字列表示
        DrawString(100,100, "こんにちは! DXライブラリ!" , White);//文字列表示
}
このように、static 変数のグローバル変数にすれば、Whiteはtest02.cpp内でしか参照出来ません。
こうすれば他のファイルから勝手に自分の変数の値を書きかえられる危険性を防げます。
・・なんてわざわざこんな方法を紹介しましたが、C++であればクラスを使うのであまり意識してこのように変数を使うことは無いでしょう。
Cであれば上記のような変数の使い方、または、他からポインタで変数を渡すような使い方をする設計を、
C++であればまずクラスを勉強して、クラスを使った設計をすると良いかと思います。

アバター
Dixq (管理人)
管理人
記事: 1661
登録日時: 13年前
住所: 北海道札幌市
連絡を取る:

Re: #ifndefで重複インクルードを防ぐ方法

#8

投稿記事 by Dixq (管理人) » 13年前

追記:
_TEST_H_ という定義についてですが、
規格上「アンダースコア2個、あるいはアンダースコアと大文字1個ずつで始まる名前は、予約語」とされているので、使わない方が良いと思います。
ややこしいので、とにかくアンダーバーで始まる定義は避けたほうが無難です。
ルールが決まっているわけではないですが、重複include防止の定義はファイル名をそのまま書いたものをよく見かけます。
今回の場合

#define TEST_H

でしょうか。ファイル名が簡単すぎて定義が単純になりすぎるなら

#define DEF_TEST_H

とかでもいいかもしれません。(これは好き好きで良いと思います)

きょーちゃん
記事: 11
登録日時: 13年前

Re: #ifndefで重複インクルードを防ぐ方法

#9

投稿記事 by きょーちゃん » 13年前

みなさん、ありがとうございます。


>softyaさん
>多重インクルードを防ぐだけですので、test01.cppとtest02.cppの両方に各々一回づつはインクルードされます。
されないとヘッダの内容が見えませんからね。

そういやそうですね^^;
重複インクルードの意味を勘違いしていました・・・

>Dixqさん
クラスですか・・・完全に未知の領域です(汗
まずはそこから勉強してみます^^
あと予約語とか何も考えず使ってました。気をつけます!


皆さん、お世話になりました。
すっきり解決しました。本当にありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る