• ベストアンサー

条件付き取り込み

C のヘッダーファイルでは、条件付き取り込みとして、 #ifndef SAMPLE #define SAMPLE /* sample.hの内容をここに書く */ #endif のように書くのが定石のようですが、これが無いと、具体的にどういう場合に不具合が出るのでしょうか。

  • ahkrkr
  • お礼率87% (568/650)

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

  • ベストアンサー
  • Wr5
  • ベストアンサー率53% (2177/4070)
回答No.2

やってみればわかりますけどね…。 標準のヘッダを書き換えるわけにはいかないでしょうから、下記のような方法とか。 # 環境依存するかも知れない。とりあえずVC++2010ExpressEditionで確認。 間にあるundefをコメントアウトしてみたりしてコンパイルしてみるといいでしょう。 #include <stdio.h> #undef _INC_STDIO #include <stdio.h> int main(int argc, char *aargv[]) {  return 0; } >これが無いと、具体的にどういう場合に不具合が出るのでしょうか。 複数のファイルでincludeしている時にハマることがあります。 header1.hで #include "sample.h" していて、 header2.hでも #include "sample.h" していて、 source.cで #include "header1.h" #include "header2.h" していた…とか。 構造体や定数定義とかが複数のファイルにまたがっていたりするといろいろインクルードすることになって、同じファイルが複数回インクルードされることになる。とかいう事態が発生する場合があります。 そんな時に威力を発揮しますね。

ahkrkr
質問者

お礼

回答ありがとうございます。 確かに、ヘッダーファイルの中でヘッダーファイルをインクルードするとそういうことが起こるかもしれませんね。

その他の回答 (2)

  • chie65535
  • ベストアンサー率43% (8520/19369)
回答No.3

Cでは「1回しか定義できないもの」や「1回しか宣言しちゃいけないもの」があります。 そして「あるヘッダーから、別のヘッダーを呼び出している」って事もあります。 例えば、stdio.hの中では、stdio.hで必要な標準の定義を、_stddef.hを呼び出して行っています。 ユーザーが、以下のように書いたら、どうなるでしょう? #include <_stddef.h> #include <stdio.h> こう書くと、_stddef.hは「1つのソースファイルで、2回呼び出される」ことになります。 ユーザーは「stdio.hの中でも呼んでるとは思ってない」ですから「_stddef.hが2回呼ばれているとは思ってない」です。 もし、_stddef.hの中に「1回しか定義できないもの」や「1回しか宣言しちゃいけないもの」があったら、どうなるでしょう? 当然「2回呼び出した段階で、コンパイルエラー」です。 ですが、質問文にあるように「条件付き取り込み」をやっておけば「2回呼び出されたら、2回目は何もしない」ので、コンパイルエラーにはなりません。 もし「ヘッダーファイルの中では、入れ子でヘッダーファイルを呼び出さない」って決めれば、こういう事が起きません。ユーザーが「明示的に2回呼び出さない限りは、2回は呼び出されない」ですから。 しかし「ヘッダーの中に、別のヘッダーを呼び出すのように書いてはいけない」とすると、もっと困った事になります。 stdio.hは_stddef.hに依存します。_stddef.hは_defs.hに依存します。 ユーザーが #include <stdio.h> と書くと、事前に_stddef.hを呼び出してないので、コンパイルエラーになります。 未定義エラーが出て、それが何処で定義されているか調べないと「事前に_stddef.hが呼び出されていないといけない」って事が判りません。 じゃあ、って言って #include <_stddef.h> #include <stdio.h> って書くと、今度は、事前に_defs.hを呼び出してないので、コンパイルエラーになります。 そして #include <_defs.h> #include <_stddef.h> #include <stdio.h> って書いて、やっとコンパイルに成功します。 これでは面倒なので「stdio.hの中で、自動的に_stddef.hを呼び出す」、「_stddef.hの中で、自動的に_defs.hを呼び出す」って言うようにしたのです。 そうすれば #include <stdio.h> って書くだけで済みますから。 もちろん、これらは、ユーザーが #include <_defs.h> #include <_stddef.h> #include <stdio.h> と書いても「2重定義エラー」にならないよう、条件付き取り込みで、2回以上取り込んでも問題無いようになっています。 このように「2重取り込みされても問題ないように書く」のです。

ahkrkr
質問者

お礼

よく分かりました。 ありがとうございました。

  • maiko0318
  • ベストアンサー率21% (1483/6970)
回答No.1

二重三重の定義でエラーになりますね。 内容は同じでも、コンパイラーには重複しているように見えますから。

関連するQ&A

  • (msvcr71d.dll) でハンドルされていない例外が発生しました

    Visual C++ を勉強しだした Windowsプログラム初心者です。 ある参考書のサンプルで、メールスロットを利用したメッセッンジャー体験プログラムがあります。 このプログラム一つで、クライアントとサーバーを実感する為のもので、ダイアログボックスだけで出来ています。 デバック実行で、画像にある[送信]ボタンを押すと、 「Mailslot.exe の 0x1021471c (msvcr71d.dll) でハンドルされていない例外が発生しました : 0xC0000005: 場所 0x00000015 を読み込み中にアクセス違反が発生しました。 。」 とエラーボックスが出ます。 原因は パソコンのOSのバージョンなのか、何か足りないライブラリなのか?切り分け所がわからなくて困っています。 どなたか良いアドバイスをお願いします。 OS : Vista Home Premium IDE: Visual C++.net スタンダード version 2003 因みに、stdafx.h の内容は↓です。 #pragma once #ifndef VC_EXTRALEAN #define VC_EXTRALEAN #endif #ifndef WINVER #define WINVER 0x0501 #endif #ifndef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #endif #ifndef _WIN32_WINDOWS #define _WIN32_WINDOWS 0x0410 #endif #ifndef _WIN32_IE #define _WIN32_IE 0x0400 #endif #define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 一般的で無視しても安全な MFC の警告メッセージの一部の非表示を解除します。 #define _AFX_ALL_WARNINGS #include <afxwin.h> #include <afxext.h> #include <afxdisp.h> #include <afxdtctl.h> #ifndef _AFX_NO_AFXCMN_SUPPORT #include <afxcmn.h> #endif #include <Lmcons.h>

  • 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を用いた方法でお願いします。

  • #ifndefとかヘッダファイルについて

    http://www.geocities.co.jp/SiliconValley/6071/technic/16.html に書いてあることで質問です。 /* myheader.h */ #ifndef __myheader_H__ #define __myheader_H__ : : : #endif と記述されているのですが、ここの「#define __myheader_H__ 」というのは、myheader.hをインクルードしているという意味になるのでしょうか? あと、ifndef等でヘッダファイルが読み込まれているか判定する際には、コンマをアンダーバー(アンダースコア)に変えるという作業は必ずいるようですが、いろんな所を調べてみると、例えば、myheader.hについて判定する場合は、 __myheader_H__ myheader_H _myheader_H _MYHEADER_H 等、アンダースコアが先頭と最後に2個付いていたり、先頭に1個だけだったり、全部大文字にしていたりと、様々なものがあるのですが、これはどの様な表記を使えばいいのでしょうか?

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

    「ローベルのC++入門講座」という本を読んでいるのですが、 その中でヘッダーファイルの2重インクルードを回避するマクロとして、 ---------------------------------------------------------------- #ifndef REDEF1_H_20070101_1138_JOIE93UL_INCLUDED_ #define REDEF1_H_20070101_1138_JOIE93UL_INCLUDED_ const int N = 10; ・ ・ ・ #endif ---------------------------------------------------------------- というサンプルがでてくるのですが、 「REDEF1_H_20070101_1138_JOIE93UL_INCLUDED_」 の意味がよくわからないです。 おそらく、 「REDEF1_H」はファイル名で、 「20070101_1138」はredef1.hの作成日時か更新日時で 「INCLUDED」は「インクルードされている」の意味だと思う のですが、 「JOIE93UL」が何を意味するのかがさっぱりです。 さほど重要なことではなさそうですが、なんだか気持ちが悪いので どなたか教えて頂ければ幸いです

  • error LNK2001について

    C++ の勉強中です。 error LNK2001問題がありました。問題点も分からないから、 分かった方に教えていただけないでしょうか? ソースも添付します。環境はVS2005 express edition まず、ヘッダファイルです。ファイル名は、myheader.h #ifndef __MYHEADER_H__ #define __MYHEADER_H__ #define includeshow(x) \ { \ s.show(x); \ } class Sample{ public: void show(char * parameter); }; extern Sample s; #endif //------------------------------------------------ 次に、Sample.cppです。 #include "myheader.h" #include <stdio.h> void Sample::show(char *parameter){ printf("in show func"); } //----------------------------------------------- 最後にTest.cppです。 #include "myheader.h" #include <stdio.h> int main(){ includeshow("ppppp"); } これで error LNK2001: 外部シンボル ""class Sample s" (?s@@3VSample@@A)" は未解決です。というエラーが出ています。 よろしくおねがいします。

  • HANDLEの宣言でのエラー

    現在C言語でプログラムを作成しています. シリアル通信を行うプログラムなのですが,エラーが出てしまい, ネットなどで検索してみたのですが,解決できませんでしたので,ご存知の方がいましたら,教えてください. エラーは以下のとおりです. [test.h] #ifndef INCLUDED_TEST_H #define INCLUDED_TEST_H HANDLE handle; HANDLE sirial(char ,char); void sirial_(HANDLE); #endif 上の用にヘッダファイルを作成し,グローバル変数の宣言・プロトタイプ宣言を行おうとすると型がHANDLEのところでエラーが出てしまいます. main関数と同じファイル内で宣言,定義した場合はエラーはでません. ヘッダファイルのように別ファイルに宣言するとエラーとなってしまうのです. 環境は Windows XP SP2 visual studio .net です. よろしくお願いします.

  • 2重定義って??

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

  • PICC  ビルドトラブル

     宜しくお願いします。  あるソースファイル(net から入手)を ビルド しましたら 次のような メッセージ が 出ました。 これは どうしてでしょうか。 usart.h の 情報 も コピー します。  Error [141] C:\Users\tadao\Desktop\1207\0702_3.c; 3.18 can't open include file "usart.h": No such file or directory  #ifndef _SERIAL_H_ #define _SERIAL_H_ #define BAUD 9600 #define FOSC 4000000L #define NINE 0 /* Use 9bit communication? FALSE=8bit */ #define DIVIDER ((int)(FOSC/(16UL * BAUD) -1)) #define HIGH_SPEED 1 #if NINE == 1 #define NINE_BITS 0x40 #else #define NINE_BITS 0 #endif #if HIGH_SPEED == 1 #define SPEED 0x4 #else #define SPEED 0 #endif #if defined(_16F87) || defined(_16F88) #define RX_PIN TRISB2 #define TX_PIN TRISB5 #else #define RX_PIN TRISC7 #define TX_PIN TRISC6 #endif /* Serial initialization */ #define init_comms()\ RX_PIN = 1; \ TX_PIN = 1; \ SPBRG = DIVIDER; \ RCSTA = (NINE_BITS|0x90); \ TXSTA = (SPEED|NINE_BITS|0x20) void putch(unsigned char); unsigned char getch(void); unsigned char getche(void); #endif  C:\Users\tadao\Desktop\1207  以上 宜しくお願いいたします。 0256  

  • ifdefとenum

    enum { INDEX_A = 0, INDEX_B, INDEX_C, INDEX_END }; #ifndef INDEX_END #define INDEX_END 10 #endif enum{}文で定義した名前は#ifndef,#ifdef文の対象外でしょうか?

  • 関数の実体定義にヘッダファイルの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を検索してみたのですが、 このような特殊な場合について記述されているものは 見つけられませんでした。 どなたか解決法又はヒントをご教示頂ければ ありがたいです。 よろしくお願いします。

専門家に質問してみよう