CおよびC++プログラミング言語では、#include ガード(マクロ ガード、ヘッダー ガード、またはファイル ガードと呼ばれることもあります) は、 include ディレクティブを処理するときに二重インクルードの問題を回避する方法です。
Cプリプロセッサ#include "foo.h"は、 「foo.h」をインクルードするなどのインクルード ディレクティブを処理し、そのファイルのコードを、翻訳単位と呼ばれることが多いメイン ファイルのコピーにトランスクルードします。
ただし、特定のファイルに対する #include ディレクティブがコンパイル中に複数回出現すると、多くの場合、構造が2 回定義され、結果として得られる翻訳単位が無効になります。#include ガードは、ヘッダーが最初にインクルードされるときにプリプロセッサ マクロを定義し、その存在を検出して次回インクルードされるときにファイルの内容を無視することで、これを防ぎます。
ヘッダー ファイルに #include ガードを追加することは、そのファイルをべき等にしてプログラムをより安全にする 1 つの方法です。二重インクルードに対抗するもう 1 つの構成要素は#pragma onceです。これは非標準ですが、C および C++コンパイラーの間でほぼ普遍的にサポートされているディレクティブです。
二重包含
例
次の C コードは、#include ガードが欠落している場合に発生する可能性がある実際の問題を示しています。
ファイル「grandparent.h」
構造体foo { intメンバー; };
ファイル「parent.h」
#include "祖父母.h"
ファイル「child.c」
#include "祖父母.h" #include "親.h"
結果
構造体foo { intメンバー; };構造体foo { intメンバー; };
ここで、ファイル「child.c」は、ヘッダー ファイル「grandparent.h」のテキストのコピーを間接的に 2 つインクルードしています。これにより、構造体型が 2 回定義されるため、コンパイル エラーが発生します。C++ では、これは1 つの定義ルールfooの違反と呼ばれます。
#include ガードの使用
例
前のセクションと同じコードが、#include ガードを追加して使用されます。Cプリプロセッサは、ヘッダー ファイルを前処理し、再帰的にインクルードしてさらに前処理します。これにより、動作するソース ファイルが生成されます。
ファイル「grandparent.h」
#ifndef GRANDPARENT_H
#define GRANDPARENT_H
構造体foo { intメンバー; };
#endif /* GRANDPARENT_H */
ファイル「parent.h」
#include "祖父母.h"
ファイル「child.c」
#include "祖父母.h" #include "親.h"
結果
構造体foo { intメンバー; };
ここで、最初の「grandparent.h」のインクルードでマクロがGRANDPARENT_H定義されています。「child.c」が 2 回目に「grandparent.h」をインクルードするとき (「parent.h」をインクルードしているとき)、#ifndefテストが false を返すため、プリプロセッサは までスキップし#endif、 の 2 番目の定義を回避しますstruct foo。プログラムは正しくコンパイルされます。
議論
ガードマクロの命名規則は、プログラマによって異なる場合があります。上記の例の他の一般的な形式には、(適切な時間情報が置き換えられます)、UUIDから生成された名前などがあります。(ただし、やなどの、 1 つのアンダースコアと大文字で始まる名前(C および C++)や、2 つのアンダースコアを含む名前(C++ のみ)は、言語実装用に予約されており、ユーザーが使用しないでください。[1] [2])
GRANDPARENT_INCLUDEDCREATORSNAME_YYYYMMDD_HHMMSS_GRANDPARENT_HGRANDPARENT__H
もちろん、異なるヘッダー ファイルで同じ include-guard マクロ名が重複しないようにすることが重要です。1 つ目のマクロをインクルードすると、2 つ目のマクロがインクルードされなくなり、2 つ目のヘッダー内の宣言、インライン定義、またはその他の #include が失われることになります。
困難
#include ガードが適切に機能するには、各ガードが異なるプリプロセッサ マクロをテストし、条件付きで設定する必要があります。したがって、#include ガードを使用するプロジェクトでは、include ガードの一貫した命名スキームを作成し、そのスキームが使用するサードパーティ ヘッダーのスキームやグローバルに表示されるマクロの名前と競合しないようにする必要があります。
このため、ほとんどの C および C++ 実装では非標準の#pragma onceディレクティブが提供されています。ヘッダー ファイルの先頭に挿入されるこのディレクティブは、ファイルが 1 回だけインクルードされることを保証します。Objective-C 言語 (C のスーパーセット) には、 と#importまったく同じように動作するディレクティブがあります#includeが、各ファイルを 1 回だけインクルードするため、#include ガードは不要です。[3]
その他の言語
一部の言語では、コードをインクルードするファイルではなく、インクルードするファイルに 1 回だけインクルードするように指定することがサポートされています (C/C++ のインクルード ガードや など#pragma once)。
- PL/I は、
%INCLUDEC のディレクティブに相当するものとして ステートメントを使用します#include。IBM Enterprise PL/I は、外部テキストが以前に含まれていない場合にソース プログラムに組み込む ステートメントもサポートしています。( ステートメントに似た ステートメント%XINCLUDEも提供されており、同じ名前のの 2 回目以降の出現を無視します。) [4]XPROCEDUREPROCEDUREXPROCEDURE - Objective-Cの
#importディレクティブ(上記参照) - PHPの
include_once[5]
参照
参考文献
- ^ C++ 標準 (ISO/IEC 14882) セクション 17.4.3.1.2/1
- ^ C規格(ISO/IEC 9899)セクション7.1.3/1。
- ^ 「Objective C: クラスの定義」。developer.apple.com 。2014年 9 月 17 日。2018 年 10 月 3 日閲覧。
- ^ IBM Corporation (2017 年 8 月). Enterprise PL/I for z/OS PL/I for AIX Enterprise PL/I for z/OS 言語リファレンス バージョン 5 リリース 1 (PDF) p. 257 . 2022 年4 月 7 日閲覧。
- ^ "include_once (PHP 言語リファレンス)"。
外部リンク
- ガード最適化を含める
- 冗長なインクルードガード
