インクルードガード

CおよびC++プログラミング言語では、 #includeガード(マクロガードヘッダーガードファイルガードと呼ばれることもあります)はincludeディレクティブを扱う際に二重インクルードの問題を回避する方法です

Cプリプロセッサは#include "Foo.h"、「Foo.h」をインクルードするなどのインクルード ディレクティブを処理し、そのファイルのコードを、翻訳単位と呼ばれることが多いメイン ファイルのコピーにトランスクルードします

ただし、コンパイル中に特定のファイルに対する#includeディレクティブが複数回出現すると、そのファイル内でコードが実質的に重複することになります。インクルードされたファイルに定義が含まれている場合、単一定義ルール(クラス定義など)によりコンパイルエラーが発生する可能性があります。このルールでは、定義(クラス定義など)は翻訳単位内で重複して記述できないとされています。#includeガードは、ヘッダーファイルが最初にインクルードされる際にプリプロセッサマクロを定義することで、この重複を防止します。ヘッダーファイルが2度目にインクルードされた場合、#includeガードは、そのヘッダー内の実際のコードがコンパイルされるのを防ぎます。

#include ガードの代替として、#pragma onceがあります。これは非標準ですが、CおよびC++コンパイラで広くサポートされているディレクティブで、#include ガードと同じ目的を持ちますが、コード量が少なく、変数の定義も必要ありません。

C++20で導入されたモジュールは#include、プリプロセッサによって処理されないため、ガードが不要になります。モジュールは、翻訳単位に最大1回しかインポートできません。

二重インクルード

次のCコードは、#includeガードがない場合に発生する可能性のある実際の問題を示しています

ファイル「Grandparent.h」

構造体Foo { intメンバー; };    

ファイル「Parent.h」

#include "Grandparent.h" 

ファイル「Child.c」

#include "Grandparent.h" #include "Parent.h"  

結果

struct Foo { // "Grandparent.h"の intメンバーから; }; struct Foo { // "Parent.h" のintメンバーから; };          

ここで、ファイル「Child.c」はヘッダーファイル「Grandparent.h」のテキストを間接的に2つインクルードしています。これにより構造体型が2回定義されるため、コンパイルエラーが発生します。C++では、これは単一定義ルールFoo違反と呼ばれます

#include ガードの使用

前のセクションと同じコードに#includeガードを追加して使用します。Cプリプロセッサはヘッダーファイルを前処理し、再帰的にインクルードしてさらに前処理します。これにより、動作するソースファイルが生成されます

ファイル「Grandparent.h」

#ifndef GRANDPARENT_H #define GRANDPARENT_H構造体Foo { intメンバー; };    #endif /* GRANDPARENT_H */

ファイル「Parent.h」

#include "Grandparent.h" 

ファイル「Child.c」

#include "Grandparent.h" #include "Parent.h"  

中間ステップ

// 「Grandparent.h」の内容#ifndef GRANDPARENT_H // GRANDPARENT_Hは定義されていません#define GRANDPARENT_Hstruct Foo { // この定義はコンパイルされたint member ; };     #endif /* GRANDPARENT_H */// "Parent.h" の内容#ifndef GRANDPARENT_H // GRANDPARENT_H はすでに定義されています#define GRANDPARENT_Hstruct Foo { // この定義はコンパイルされていませんint member ; };     #endif /* GRANDPARENT_H */

結果

構造体Foo { intメンバー; };    

ここで、「Grandparent.h」の最初のインクルードでマクロがGRANDPARENT_H定義されています。「Child.c」が「Grandparent.h」を2回目にインクルードするとき(「Parent.h」をインクルードしている間)、#ifndefテストは偽を返すため、プリプロセッサは までスキップし#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ディレクティブが提供されています。このディレクティブはヘッダーファイルの先頭に挿入され、ファイルが一度だけインクルードされることを保証します。Objective-C言語(C言語のスーパーセット)には#importディレクティブがあり、これは と全く同じように動作します#includeが、各ファイルを一度だけインクルードするため、#includeガードは不要です。[3]

その他の言語

一部の言語では、コードをインクルードするファイルではなく、インクルードするファイルに1回だけインクルードするように指定できます(C/C++のインクルードガードやのように#pragma once)。

  • PL/Iでは%INCLUDE、C言語のディレクティブに相当する文として 文を使用します#include。IBM Enterprise PL/Iでは、%XINCLUDE外部テキストが以前に組み込まれていない場合にそれをソースプログラムに組み込む 文もサポートしています。(またXPROCEDURE、 文に似た 文も提供されており、これは同じ名前のPROCEDURE文の2回目以降の出現を無視します。) [4]XPROCEDURE
  • Objective-C#importディレクティブ(上記参照)
  • PHPinclude_once[5]

参照

参考文献

  1. ^ C++標準(ISO/IEC 14882)セクション17.4.3.1.2/1
  2. ^ C規格(ISO/IEC 9899)セクション7.1.3/1。
  3. ^ 「Objective C: クラスの定義」developer.apple.com . 2014年9月17日. 2018年10月3日閲覧
  4. ^ 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日閲覧
  5. ^ "include_once (PHP言語リファレンス)".
  • インクルードガードの最適化
  • 冗長なインクルードガード
「https://en.wikipedia.org/w/index.php?title=Include_guard&oldid=1304369513」より取得