• ベストアンサー

関数の実体定義にヘッダファイルの2重定義防止方法が効かない?

いつもお世話になっています。 MFCでCプログラミングをしています。 ヘッダファイルの2重定義防止のために、 ヘッダファイル全体を下記のように 囲みました。 <aaa.h> #ifndef AAA #define AAA #define PI 3.141592 void Func(); int wa(int a, int b){ return a+b; } #endif ビルドしたところ、 関数宣言(Func)や#define部分(PI)については、 2重定義が防止されているようなのですが、 関数の実体部分(関数wa)については、 2重定義防止機能が働かず、 ***.obj : error LNK2005: "int __cdecl wa(int a, int b)" は既に ***.obj で定義されています。 というリンクエラーが表示されます。 関数の種類や ヘッダファイル内の宣言の順番を いろいろ変えてみたのですが同じ結果でした。 ここで、このヘッダファイルの先頭に #pragma onceを使用すると このリンクエラーは回避されるのですが、 他コンパイラとの互換性の観点から、 #pragma once以外の方法で実現する必要があるので、 困っています。 URLを検索してみたのですが、 このような特殊な場合について記述されているものは 見つけられませんでした。 どなたか解決法又はヒントをご教示頂ければ ありがたいです。 よろしくお願いします。

  • zico2
  • お礼率79% (27/34)

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

  • ベストアンサー
  • gimmick
  • ベストアンサー率49% (134/270)
回答No.2

二重インクルードは防止できています。ただし、あくまで1つのコンパイル単位の中での話です。エラー内容から察すると、2つ以上のソースファイルでaaa.hをインクルードしていますね?コンパイル時点ではエラーにはならずに複数のobjファイルが生成されますが、リンク時にエラーが発生します。これは、リンク実行時に関数waの定義が複数のモジュールで発見されるためです。 このようなエラーを防ぐため、通常、ヘッダファイルで関数の定義は行わず、その代わりに extern int wa(int a, int b); のように宣言だけを記述します。関数定義はどこかのソースファイルで1回だけ行います。

zico2
質問者

お礼

定義部分を別ファイルにすることにしました。 その結果、リンクエラーは解決できました。 ご教示ありがとうございました。

その他の回答 (4)

  • MovingWalk
  • ベストアンサー率43% (2233/5098)
回答No.5

#2、#4さんのご指摘どおりです。 インクルードファイルに関数定義を記述するのは好ましくありません。 通常は、関数定義ではなく関数プロトタイプ宣言を記述します。 関数定義は別のソース(wa.c)などに記述し、リンクしてください。

zico2
質問者

お礼

ご教示ありがとうございました。

  • bikkuri
  • ベストアンサー率33% (23/68)
回答No.4

#2の方の通りかと思います。 aaa.hファイルが問題のヘッダーファイルとして、 ソースファイルがa.c, b.cの2つあるとすると a.cでは、 #inlucde "aaa.h" ...... b.cでも #include "aaa.h" ...... とされていると思います。 すると2つのソースから作成されるa.obj,b.objそれぞれに 関数waが定義されており、しかもグローバルなスコープです。 よって、リンク時にwaが2つあるのでエラーになっていると思います。 対処としても、#2の方と同じく 関数waはどこかのソースに実体を記述し、ヘッダーファイルに 関数のプロトタイプを記述するのが通常です。 どうしてもヘッダーファイルに関数の実体を記述したいのでしたら スコープをstaticにすればエラーを回避できるでしょう static int wa(int a, int b){ return a+b; } でもこれは普通じゃないです。

zico2
質問者

お礼

丁寧な解説を頂き、 エラー原因のメカニズムが良く分かりました。 最終的には、 普通でない方法は辞めることにし、 うまくビルド成功しました。 ご教示ありがとうございました。

  • yatokesa
  • ベストアンサー率40% (201/496)
回答No.3

ここに書かれているソースでは#define AAAの前にスペースが無いですが、配信されてきたメールにはスペース(TAB?)がありました。 私の記憶では、#defineは先頭から始まっていなければならなかったという思います。このため AAAが定義されずに重複エラーとなったのではないでしょうか?

zico2
質問者

お礼

ご教示ありがとうございました。 スペースについては、特に問題ないようでした。

回答No.1

ヘッダに関数の'定義'があるのがヘンです。 多くの場合リンク時にエラーとなるでしょう。

zico2
質問者

お礼

ご教示ありがとうございました。

関連するQ&A

  • C言語でヘッダファイルを自作する

    C言語で#defineを用いてヘッダファイルを作成したのですが、 作成したコンパイルするときにヘッダファイルがオープンできません。 参考にしている資料があるのですが、そこに書かれているサンプルプログラムを 丸ごとコピーして作ったプログラムも同様にヘッダファイルがオープンできない というエラーが出るので、ヘッダファイルを定義する方法そのものが間違っていると 思うのですが、どこが間違っているのでしょうか? よろしければ正しい記述方法もお教えください。 #include <stdio.h> #if !defined SAMPLE_H #define SAMPLE_H wa(int a, int b) { return a+b; } #endif #include "sample.h" int main(){ printf("%d\n",wa(40,70)); return 0; } ヘッダファイルの定義の方法は他にもあるとは思いますが、 今回は#defineを用いた方法でお願いします。

  • ヘッダーファイルについて

    //DMusic7Ex.h あるプログラムを解析しています。 //コンパイラ設定 //多重定義防止 #pragma once //ファイルインクルード #include <dmusicc.h> #include <dmusici.h> //型定義 typedef IDirectMusicPerformance* LPDIRECTMUSICPERFORMANCE; typedef IDirectMusic* LPDIRECTMUSIC; typedef IDirectMusicLoader* LPDIRECTMUSICLOADER; typedef IDirectMusicSegment* LPDIRECTMUSICSEGMENT; //関数プロトタイプ 1・DMusic7Ex.cppを作らず他の関数で、このヘッダーファイルをインクルードする手法は一般的なのか? 2・なぜ関数プロトタイプを記述しないで、このタイプ宣言を他のファイルで使用するのは一般的なのか?

  • 関数の定義

    度々すみません、教えてください。。。 func(int a,int b,int c,int d); func(int a,int b); func(int a); っていう関数を使いたい場合、関数の定義(宣言?)はどのように書けばよいのですか?エラーになってしまいます・・・。

  • 他の.CPPファイルに定義した関数を呼び出す方法について

    新規作成したプロジェクトに、 以前自分が作成した.cppファイルと.hファイルを そのまま使えないかと考えています。 (※仮にそのファイルの名前を "define.cpp" "define.h" とします) プロジェクトに新規ファイル main.cpp を作成して、 define.hをインクルードし、 以下のようなテストのプログラムを組みました。 ・../util/define.cpp---------------------- #include <iostream> #include "define.h" void test(){ std::cout<<"test."<<std::endl; } ・../util/define.h------------------------ #pragma once void test(); ・main.cpp---------------------------- #include "../util/define.h" int main(){  test();  return 0; } 上記のソースを VisualC++7.0 でビルドすると、 main.cppの3行目で以下のようなエラーが出ました。 > LNK2019: 未解決の外部シンボル "void __cdecl test(void)" が関数 _main で参照されました VisualC++で「既存項目の追加」という項目より、 実体の定義されたdefine.cppをプロジェクトに追加していないため 当然といえば当然なのですが・・・ C言語でいうところの<stdio.h>等みたいに、 わざわざプロジェクトにCPPファイルを追加しなくても 関数を呼び出せるようには出来ないのでしょうか? 全ての関数と処理をヘッダーファイルに記述すると解決ですが 物凄く見辛いのでそれは避けたいのです。 また、色々なPC間で使っているため(学校のPCなので)、 ツール自体のプロパティを弄らない方法があるのでしたら、 多少面倒でもそちらの方が好ましいです。 追加する方法があるかどうか、 あればその方法をご存じでしたら教えていただければ嬉しいです。 よろしくお願いします。

  • 別ファイルで定義した関数を使用するwindows

    「C言語によるプログラミング 応用編 第2版」という参考書でCの勉強をしています。 この本は、unix と windows 両方のコンパイラを対象に解説していますが、 私は今 VC++ 2005 に付与される cl.exe と link.exe で勉強を進めています。 ( os:WindowsXP home ) 下記の(1)のような関数のみを定義したソース内の "snapshot関数"を、 下記の(3)のような他のmain関数を持った実行コードから使用したい場合 コンパイル・リンクのケースで、どんな手順でコンパイル・リンクすればよいのでしょう? (1) 関数のみを定義した"snapshot.c" #include <stdio.h> ... 省略 ... /* エラーmessage , ワーニングmessage , ノーマルmessage を選んで出力する関数 */ int snapshot( int type , char *fmt, ... ) { ... 省略 ... } (2) 上の関数定義ファイルのヘッダファイル"snapshot.h" extern int snapshot( int type , char *fmt, ... ); #define NORMAL 0 #define WARNING 1 #define ERROR 2 (3) 上のsanpshot関数を使用する"use_snapshot.c" #include <stdio.h> #include "snapshot.h" /* これで snapshot関数を使用できる */ int main( void ) { int i = 10; snapshot( NORMAL, "normal message. %d\n", i ); snapshot( WARNING, "warning message. %d\n", ++i ); snapshot( ERROR, "error message. %d\n", ++i ); return 0; } 私が、まず(1)の"snapshot.c"のみをコンパイル >cl /c sanpshot.c した後、 "snapshot.obj"をリンクしたときのエラーは 添付画像"err_link_snapshot.obj.JPG" です。 この意味もよく分からないのですが、 次に、"use_snapshot.obj" と "snapshot.obj" をまとめてリンクしようとした時のエラーは、下記です。 >link use_snapshot.obj snapshot.obj snapshot.obj : error LNK2019: 未解決の外部シンボル _vspritf が関数 _snapshot で 参照されました。 use_snapshot.exe : fatal error LNK1120: 外部参照 1 が未解決です。 初歩的な問題かと思いますが、どなたかこの解決法を ご教示いただけないでしょうか? 参考サイトなども教えていただけると有難いと思います。 よろしくお願いします。

  • 相互のインクルードファイルの方法について

    下のように、2つのヘッダーファイルを相互にインクルードすると colorparettewindowclass.h(2) : fatal error C1014: インクルード ファイルが多すぎます : 深さ = 1024 というエラーが表示されます。(2つ表示される) 一応、#pragma onceを使用していますが、 エラーになります。 どうすればいいでしょうか? よろしくお願いします。 --------------------------------------------- <<ColorParetteWindowParentClass.h>> #include"WindowBaseClass.h" #include"ColorParetteWindowClass.h" #include"ColorClass.h" #pragma once class ColorParetteWindowParentClass : public WindowBaseClass { ((処理関数等定義)) }; --------------------------------------------- <<ColorParetteWindowClass .h>> #include"WindowBaseClass.h" #include"ColorParetteWindowParentClass.h" #pragma once class ColorParetteWindowClass : public WindowBaseClass { ((処理関数等定義)) };

  • ヘッダーの多重定義での疑問です。

     Cで開発をしているのですが、ヘッダーファイルでの定義ではたとえば、int a と か double hoge とか定義して、そのヘッダーファイルを複数の cファイルが読み込むと多重定義になりますよね? hoge.h int a; // 多重定義 このヘッダーを複数の.cファイルに読み込む しかし、自分のプロジェクトだと、ヘッダーファイルにint aとかdouble hogeとか 定義してもエラーにならないんです。 ただし、int b = 10;のように代入すると多重エラー また、試しに新しくプロジェクトを作ってヘッダーで定義すると多重定義でエラーでした。

  • ヘッダファイルの2重のインクルードについて

    ある書物に「ヘッダファイルを複数回インクルードすると、それを”再定義”することによるエラーになる」と書いてありますたが、以下のようなヘッダファイルを作りそれを数回インクルードしても何ら異状が無くコンパイルできました。 math.h→ヘッダファイル名 #define max(a,b)  ((a)>(b)?(a):(b))→ヘッダファイルの内容 c ファイル #include<stdio.h> #include"math.h"←複数回インクルード #include"math.h"←複数回インクルード int main(void){ int x=1,y=2; printf("max(x,y)=%d\n",max(x,y)); return 0; } 環境としては、RedHat Linuxでviです。 何ゆえ、エラーにならないのか良く解りません。 宜しくお願いします。

  • 2重定義って??

    C言語のプログラミングの勉強をしています。 そこで2重定義というものを知り調べたのですが、良く分かりませんでした。コンパイルの仕組みなども併せて教えてください。お願いいたします。 恐れ入りますが、どなたか初心者にも分かる位のレベルで教えて頂けますでしょうか? 簡単な例があると助かります。 不明点 ・2重定義とは例えば1つの*.hを2つ以上の*.cでインクルードする場合にのみ有効なのか? 自分で調べた結果 2重定義防止用として #ifndef HOGE #define HOGE ~~~~~~~~ #endif 上記のようなことを一般的には行うことは分かったのですが、 これをやったことでどうなるのか??

  • ヘッダファイルに関数本体を書き込めないのか?

    こんにちは。 現在WindowsVistaでCおよびC++を使ってプログラミングを行っています。 最近になって思ったのですが、普通、ヘッダファイルに記述する内容は、 ・関数のプロトタイプ ・クラスのメンバ関数を除いた部分(いわゆる「クラスの骨格」) ・マクロ といったものだと言われています。 そして、関数の実態やクラスのメンバ関数などは、 別のソースファイルに記述するように言われています。 なぜ、ヘッダファイルに関数の実態や、クラスのメンバ関数を記述するべきではないでしょうか? あるいは、プログラムの内容に応じて、関数やクラスの内容を、 ヘッダファイルにまるごと記述してもよい場合と悪い場合があるのでしょうか? こういった事について、何か御存じの方がいらっしゃれば、是非アドバイスをお願い致します。 (難しい問題なので、なるべく詳しい説明を頂けると、大変助かります。) ちなみに、関数やクラスのメンバ関数も一緒に、クラスの内容をまるごとヘッダファイルに記述しても、 今までの所、全く問題なく動作しています。 例えば、以下のようなプログラムは、何の問題もなく動作します。 ●main.c ____________________________________________________________ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> #include "func.h" void main(void) { char str[80]; puts("文字列を入力せよ"); gets(str); func(str); } ____________________________________________________________ ●func.h ____________________________________________________________ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <string.h> void func(char *p) { puts("入力された内容は以下の通り。"); while(*p) putchar(*p++); } ____________________________________________________________

専門家に質問してみよう