みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

多重定義が起きている?--lnk2005エラー:VC++

今つまずいている問題は、VC++2008環境下で、以下のような構造になっているプログラムにおいて、多重定義?されているのでコンパイルが通らないというものです。

ソースコードを載せることは大きすぎてできないので、問題となる部分の記述のみ抽出して書きます。

-------define.h----------
・・・・(module.h内の関数に使われる型の定義など)
---------------------------

-------module.h----------
#include "define.h"
void mod_1(void){・・・}
void mod_2(void){・・・}
---------------------------

このように、2つのヘッダーファイルがあり、define.hをインクルードしてmodule.hを使うという構造です。
(一般的ではないようですが、module.h内にmod_1やmod_2の実体を書き込んでいます。)

その中で、以下のようなcppソースファイルがあります。

-------Main.cpp----------
#include "module.h"   (他のMain.cpp内の関数の都合上、Main.cppでもmodule.hをインクルードしています。関係あるかもしれないと思い書きました。)
int main(){
M();
N();
}
------------------------

-------M.cpp----------
#include "module.h"
void M(void){
mod_1();
mod_2();
}
----------------------

-------N.cpp----------
#include "module.h"
void N(void){
mod_1();
mod_2();
}
----------------------

とすると、コンパイルの結果は次のようになります。

1>N.obj : error LNK2005: "void __cdecl mod_1(void)" (?mod_1@@YAXXZ) は既に M.obj で定義されています。
1>N.obj : error LNK2005: "void __cdecl mod_2(void)" (?mod_2@@YAXXZ) は既に M.obj で定義されています。

多重定義や多重インクルードは起こしていないと思っていますが、M.cppとN.cpp内の定義が衝突する理由がわかりません。どなたか理由が思いつく方、教えていただけたら幸いです。お願いいたします!!

◆◆◆   ◆◆◆   ◆◆◆   ◆◆◆   ◆◆◆

ちなみに、たとえばMやNとまったく同じ「A.cpp」を作り、このように書いたとします。
-------A.cpp----------
#include "module.h"
void A(void){
mod_1();
mod_2();
}
----------------------
そしてmain関数の中に、MやNと同様に『A();』を付け加えたとします。

すると、実行結果はこのようになります。

1>M.obj : error LNK2005: "void __cdecl mod_1(void)" (?mod_1@@YAXXZ) は既に A.obj で定義されています。
1>M.obj : error LNK2005: "void __cdecl mod_2(void)" (?mod_2@@YAXXZ) は既に A.obj で定義されています。

1>N.obj : error LNK2005: "void __cdecl mod_1(void)" (?mod_1@@YAXXZ) は既に A.obj で定義されています。
1>N.obj : error LNK2005: "void __cdecl mod_2(void)" (?mod_2@@YAXXZ) は既に A.obj で定義されています。

VC++はファイル名のアルファベット順にコンパイルするようですが、どちらにせよmain関数内で呼び出されていること以外何の関わりも無いはずのA、M、Nの中での出来事が、衝突する理由が、調べども思い当たりません。

お願いいたします。

投稿日時 - 2010-04-16 19:16:13

QNo.5830551

すぐに回答ほしいです

質問者が選んだベストアンサー

インクルードファイルは#include宣言した部分に展開されます。

extern宣言等は単なる外部参照なので重複しても問題はありませんが、ヘッダファイルにソースの実体が書かれている場合は、インクルードしたすべてのソースファイルに同じ関数が展開され、実体が出来ます。
つまり、
#include "module.h"
と書かれているすべてのファイルに
void mod_1(void){・・・}
void mod_2(void){・・・}
が書かれていることと同じなので、リンク時に多重定義でエラーになります。

一般的には「module.h」には
extern void mod_1(void);
extern void mod_2(void);
の外部参照だけを記載し、関数の実体は別のソースファイルに記述します。

投稿日時 - 2010-04-16 20:16:12

お礼

No.1さんに引き続き、有難うございました!提示していただいた方法で解決しました!

少しCに詳しくなれました。有難うございます。

投稿日時 - 2010-04-20 19:32:38

このQ&Aは役に立ちましたか?

10人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.4

良い方法かどうかはわかりませんが一応回避する方法として・・・

module.hに定義しているmod1関数とmod2関数をインラインとして定義する方法があります。

void mod_1(void){・・・}
void mod_2(void){・・・}

これを

inline void mod_1(void){・・・}
inline void mod_2(void){・・・}

こうすると、関数本体が何度出てきても多重定義にはならなくなります。本来は別の目的で使うキーワードですが・・・

投稿日時 - 2010-04-20 11:40:53

お礼

有難うございます!

なるほどそんな手が・・・。
Cって本当にややこしいというか、一つの体系としてC全体を捉えるには時間がかかるものですね。

投稿日時 - 2010-04-20 19:37:43

ANo.3

>(一般的ではないようですが、module.h内にmod_1やmod_2の実体を書き込んでいます。)

やっぱり、ヘッダーファイルに関数の実体を書くのは
よくないと思います。

投稿日時 - 2010-04-16 21:52:25

お礼

今回のことで、何故ヘッダーに実体を書くのが一般的でないのかがよくわかりました。以前はLinux上においてgccのゆるゆるコンパイラーだったのでVC++に乗り換えてからは悪戦苦闘です。今回のことで少し、C/C++の書き方を理解できた気がします。

投稿日時 - 2010-04-20 19:28:44

ANo.1

そりゃそうでないの?

module.hに
mod_1();
mod_2();
の実体がある訳でこれをインクルードした

Main.cpp
N.cpp
M.cpp
それぞれに、
mod_1(){・・・}
mod_2(){・・・}
があることになる訳ですね!?

となれば、

Main.obj->mod_1()
N.obj->mod_1()
M.obj->mod_1()

3つのobjをリンクしたら?
同じ部屋に同姓同名のmod_1さんが3人いる訳で…
リンカーさんも、これは困ったぞ! ってことになりませんか?

mod_1()、mod_2() を static にすればエラーにならないと思います

違うかな???
間違ったらごめんね

投稿日時 - 2010-04-16 20:00:12

お礼

解決しました!!ヘッダの中身をextern宣言だけにするだけで、いとも簡単に・・・。

しかし、なるほど、です。インクルードの意味、というかC言語のソースの読み方そのものを勘違いしていたようです。有難うございました。

投稿日時 - 2010-04-20 19:31:25

あなたにオススメの質問