C言語関係掲示板

過去ログ

No769 2重インクルードについて

[戻る] [ホームページ]
No.189

2重インクルードについて
投稿者---uzkt(2003/07/05 20:25:17)


C言語初心者です。
書籍にヘッダファイルの2重インクルードは問題なので、インクルードガードを
するべきだと書かれていますが、2重にインクルードしてみてもコンパイルから
エラーは出ません。

個人的に分割コンパイル時に用意するヘッダファイルには、
1.ソースに必要なヘッダファイルのinclude命令
2.参照される型、マクロの定義
3.参照される関数・変数の外部参照宣言
を含めるようにしています。

2重インクルードは何が問題なのでしょうか?
問題も起こっていないのに愚かな質問とは存じますが、
どなたかご教示くだされば幸甚です。

使用したソースは以下の3ファイルです。
Windows Meで、BorlandC++5.5と、LSIC試食版を使ってコンパイルしてもエラー
がでずに正常にコンパイルされました。

/***tst.c***/
#include <stdio.h>
#include "sub.h"

#define MAX 5

int main( void ) {
    char str[ MAX ];

    printf( "数字:" );
    fgets( str, MAX - 1, stdin );
    func( str );
    printf( "入力された数字:%d\n", num );

    return 0;
}

/***sub.c***/
#include <stdlib.h>
#include "sub.h"
#include "sub.h"    //ここでsub.hを2重にインクルード

int num = 0;
void func( char *pc ) {
    num = atoi( pc );
}

/***sub.h***/
extern int num;
void func( char *pc );


どうかよろしくお願いします。

No.191

Re:2重インクルードについて
投稿者---PSB(2003/07/05 23:13:44)


>2.参照される型、マクロの定義

2が問題になります。外部参照宣言は基本的にいくつあってもエラーに
はなりませんが、マクロや型宣言は複数あるとエラーになります。


No.194

Re:2重インクルードについて
投稿者---uzkt(2003/07/06 16:10:58)


PSB様
ご回答ありがとうございます。助かります。

>>2.参照される型、マクロの定義
>2が問題になります。外部参照宣言は基本的にいくつあってもエラーに
>はなりませんが、マクロや型宣言は複数あるとエラーになります。

typedefで型を作って型宣言を2重インクルードした場合、エラーを見ることができました。
ただし、以下のようにヘッダファイルの中身をマクロだけにして2重インクルードした場合ではエラーが出ないようです。
どのようなマクロの場合問題があるか教えて頂けませんでしょうか。

後、明確に2重インクルードについて書かれている情報源をご存知でしたら、ご教示いただけると幸いです。
いろいろ質問してすみません。K&Rなどを読んでみたのですが、明確な情報が分からないもので...
ヒントだけでもよいのでよろしくお願い致します。

/***sub.h***/
#define MAX 5
#define PRINT() printf( "pnt[%d] x:%d y:%d\n", idx, array[ idx ].x, array[ idx ].y )

/***tst.c***/
#include <stdio.h>
#include "sub.h"

typedef struct pnt_t {
	int	x;
	int	y;
} pnt;

void func( pnt *p_ptr );

pnt array[ MAX ];

int main( void ) {
	int idx;

	func( array );
	for ( idx = 0; idx < MAX; idx++ )
		PRINT();

	return 0;
}

/***sub.c***/
#include "sub.h"
#include "sub.h" 
//ここで2重インクルード
typedef struct pnt_t {
	int	x;
	int	y;
} pnt;

void func( pnt *p_ptr );

void func( pnt *p_ptr ) {
	int idx;

	for ( idx = 0; idx < MAX; idx++ ) {
		p_ptr[ idx ].x = idx;
		p_ptr[ idx ].y = idx;
	}
}


No.197

Re:2重インクルードについて
投稿者---PSB(2003/07/06 20:56:48)


>どのようなマクロの場合問題があるか教えて頂けませんでしょうか。

ごめんなさい、嘘でした。同じマクロはエラーにならないですね。
#define MAX 10
#define MAX 11
みたいな場合は当然エラーですが、2重インクルードとは関係ないし。

型宣言(struct、union、enum)だけみたいですね。
適当なことを書いて申し訳ないです。

No.199

Re:2重インクルードについて
投稿者---YuO(2003/07/06 21:49:11)


>ただし、以下のようにヘッダファイルの中身をマクロだけにして2重インクルードした場合ではエラーが出ないようです。
>どのようなマクロの場合問題があるか教えて頂けませんでしょうか。

マクロで問題になるのは,#ifを使っていて,条件が二回の取り込みにおいて条件が異なる場合です。

次のコードは問題になりません。
#define NULL ((void *)0)
それに対して,
#ifndef FOO_VALUE
#define FOO_VALUE
#define BAR 0
#else
#define BAR 1
#endif
はBARの定義が異なるのでエラーです。

マクロの再定義は,次の条件を満たす必要があります。
#JIS X 3010:1993 6.8.3 マクロ置換えから抜粋
・オブジェクト形式マクロ
  • 2番目の定義がオブジェクト形式マクロの定義である。
  • 二つの置換要素並びが同一である。
・関数形式マクロ
  • 仮引数の個数とつづりが最初の定義と同一な関数形式マクロの定義である。
  • 二つの置換要素並びが同一である。


>後、明確に2重インクルードについて書かれている情報源をご存知でしたら、ご教示いただけると幸いです。
>いろいろ質問してすみません。K&Rなどを読んでみたのですが、明確な情報が分からないもので...

2重インクルードではなく,多重定義などの問題になります。

ISO/IEC 9899:1999の6.9 External definitionに,
Constraints
3 There shall be no more than one external definition for each identifier declared with internal linkage in a translation unit. Moreover, if an identifier declared with internal linkage is used in an expression (other than as a part of the operand of a sizeof operator whose result is an integer constant), there shall be exactly one external definition for the identifier in the translation unit.
Semantics
5 Anexternal definition is an external declaration that is also a definition of a function (other than an inline definition) or an object. If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.
と書いてあります。つまり,
  • 内部結合の識別子の定義は翻訳単位中に一つだけ
  • 外部結合の識別子の定義はすべての翻訳単位中に一つだけ
ということになります。

構造体などに関しては,
6.7.2.3 Tags
Constraints
1 A specific type shall have its content defined at most once.
となっていて,翻訳単位一つについて一つの構造体等の定義は一度だけしかできません。


インクルードガードは,これらを避けるために行います。
つまり,インクルードガードありきではなく,多重定義の問題があるのでインクルードガードを行う,ということです。
#C++だとプログラミング言語C++に「これはヘッダファイルに書く」「これは書かない」という指針が載っているのですが……。


No.204

Re:2重インクルードについて
投稿者---uzkt(2003/07/06 23:44:59)


PSB様、YuO様
ご丁寧に返答いただき、ありがとうございました。
疑問をすっかり氷解させていただき、感謝感激です。

YuO様
JISの規格書や、ISO対応の辞書などを今度参照してみようと思います。
1つ勉強になりました。
どうも、ありがとうございました。