web-dev-qa-db-ja.com

ヘッダーに#includeを使用する必要がありますか?

ヘッダー(* .h)内でこのファイルで定義されたタイプが使用されている場合、#includeファイルが必要ですか?

たとえば、GLibを使用しており、ヘッダーで定義された構造でgchar基本型を使用する場合、*に既にあることを知って、#include <glib.h>を実行する必要があります。 cファイル?

はいの場合は、#ifndef#defineの間、または#defineの後に挿入する必要がありますか?

68
Victor

NASAのゴダード宇宙飛行センター( [〜#〜] gsfc [〜#〜] )Cのヘッダーのルールは、ソースファイルにヘッダーを唯一のヘッダーとして含めることが可能である必要があることを示しています。そのヘッダーが提供する機能を使用するコードはコンパイルされます。

これは、ヘッダーが自己完結型でべき等的で最小限でなければならないことを意味します。

  • self-contained—必要に応じて、関連するヘッダーを含めることにより、すべての必要なタイプが定義されます。
  • idempotent—コンパイルが複数回含まれていても、コンパイルは中断しません。
  • minimal—ヘッダーで定義された機能にアクセスするためにヘッダーを使用するコードで不要なものは何も定義しません。

このルールの利点は、誰かがヘッダーを使用する必要がある場合、他のどのヘッダーも含める必要があるかを見つけるために苦労する必要がないことです。

考えられる欠点は、一部のヘッダーが何度も含まれることがあることです。これが、複数のインクルードヘッダーガードが重要である理由です(また、コンパイラーは可能な限りヘッダーを再インクルードしないようにしています)。

実装

このルールは、ヘッダーが「FILE *」や「size_t」などのタイプを使用する場合、適切な他のヘッダー(<stdio.h>または<stddef.h>例)含まれる必要があります。忘れられがちな結果は、パッケージを使用するためにパッケージのユーザーが必要とするnotヘッダーをヘッダーに含めないことです。言い換えれば、ヘッダーは最小限でなければなりません。

さらに、GSFCルールは、これが起こることを確実にするための簡単な手法を提供します。

  • 機能を定義するソースファイルでは、ヘッダーはリストの最初のヘッダーである必要があります。

したがって、マジックソートがあるとします。

magicsort.h

#ifndef MAGICSORT_H_INCLUDED
#define MAGICSORT_H_INCLUDED

#include <stddef.h>

typedef int (*Comparator)(const void *, const void *);
extern void magicsort(void *array, size_t number, size_t size, Comparator cmp);

#endif /* MAGICSORT_H_INCLUDED */

magicsort.c

#include <magicsort.h>

void magicsort(void *array, size_t number, size_t size, Comparator cmp)
{
    ...body of sort...
}

ヘッダーには、size_tを定義する標準ヘッダーを含める必要があることに注意してください。これを行う最小の標準ヘッダーは<stddef.h>ですが、他のいくつかも同様です(<stdio.h><stdlib.h><string.h>、場合によっては他のいくつか)。

また、前述したように、実装ファイルに他のヘッダーが必要な場合は、そうする必要があります。また、いくつかの余分なヘッダーが必要になることは完全に正常です。ただし、実装ファイル( 'magicsort.c')にはそれらを含める必要があり、ヘッダーを使用して含める必要はありません。ヘッダーには、ソフトウェアのユーザーが必要とするもののみを含める必要があります。実装者が必要とするものではありません。

構成ヘッダー

コードで構成ヘッダー(GNU Autoconfおよび生成された「config.h」など)を使用している場合、「magicsort.c」でこれを使用する必要があります。

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include "magicsort.h"

...

これは、モジュールのプライベートヘッダーが実装ファイルの最初のヘッダーではないことを知っている唯一の時間です。ただし、「config.h」の条件付きインクルードは、おそらく「magicsort.h」自体に含まれている必要があります。


更新2011-05-01

上記のリンク先のURLは機能しなくなりました(404)。 C++標準(582-2003-004)は EverySpec.com ;にあります。 C標準(582-2000-005)は実際には欠落しているようです。

C標準のガイドラインは次のとおりです。

§2.1ユニット

(1)コードは、ユニットまたはスタンドアロンのヘッダーファイルとして構成されます。

(2)ユニットは、単一のヘッダーファイル(.h)と1つ以上の本体(.c)ファイルで構成されます。ヘッダーファイルと本文ファイルをまとめてソースファイルと呼びます。

(3)ユニットヘッダーファイルには、クライアントユニットが必要とするすべての関連情報が含まれます。ユニットのクライアントは、ユニットを使用するためにヘッダーファイルのみにアクセスする必要があります。

(4)ユニットヘッダーファイルには、ユニットヘッダーに必要な他のすべてのヘッダーの#includeステートメントが含まれます。これにより、クライアントは単一のヘッダーファイルを含めることでユニットを使用できます。

(5)ユニット本体ファイルには、他のすべての#includeステートメントの前に、ユニットヘッダーの#includeステートメントを含める必要があります。これにより、コンパイラは、必要なすべての#includeステートメントがヘッダーファイルにあることを確認できます。

(6)本体ファイルには、1つのユニットに関連付けられた機能のみが含まれます。 1つのボディファイルは、異なるヘッダーで宣言された関数の実装を提供しない場合があります。

(7)特定のユニットUの一部を使用するすべてのクライアントユニットには、ユニットUのヘッダーファイルが含まれます。これにより、ユニットUのエンティティが定義される場所は1つだけになります。クライアントユニットは、ユニットヘッダーで定義された関数のみを呼び出すことができます。本体で定義されているが、ヘッダーで宣言されていない関数を呼び出すことはできません。クライアントユニットは、本文では宣言されているがヘッダーでは宣言されていない変数にはアクセスできません。

componentには1つ以上のユニットが含まれます。たとえば、数学ライブラリは、ベクトル、行列、四元数などの複数のユニットを含むコンポーネントです。

スタンドアロンヘッダーファイルには、関連するボディがありません。たとえば、一般的なタイプのヘッダーは関数を宣言しないため、本文は必要ありません。

ユニットに複数のボディファイルがある理由:

  • ボディコードの一部はハードウェアまたはオペレーティングシステムに依存していますが、残りは一般的です。
  • ファイルが大きすぎます。
  • このユニットは一般的なユーティリティパッケージであり、一部のプロジェクトでは一部の機能しか使用しません。各関数を個別のファイルに配置すると、リンカは最終イメージから使用されていない関数を除外できます。

§2.1.1ヘッダーには理論的根拠が含まれる

この標準では、ユニットのヘッダーに必要な他のすべてのヘッダーの#includeステートメントを含むユニットのヘッダーが必要です。ユニットヘッダーの#includeをユニット本体の最初に配置すると、コンパイラーはヘッダーに必要な#includeステートメントがすべて含まれていることを確認できます。

この規格で許可されていない代替設計では、ヘッダーに#includeステートメントを使用できません。すべての#includesはbodyファイルで行われます。ユニットヘッダーファイルには、必要なヘッダーが適切な順序で含まれていることを確認する#ifdefステートメントが含まれている必要があります。

代替設計の利点の1つは、本体ファイルの#includeリストがmakefileで必要な依存関係リストとまったく同じであり、このリストがコンパイラーによってチェックされることです。標準設計では、ツールを使用して依存関係リストを生成する必要があります。ただし、ブランチが推奨する開発環境はすべて、このようなツールを提供しています。

代替設計の主な欠点は、ユニットの必須ヘッダーリストが変更された場合、そのユニットを使用する各ファイルを編集して#includeステートメントリストを更新する必要があることです。また、コンパイラライブラリユニットに必要なヘッダーリストは、ターゲットによって異なる場合があります。

代替設計のもう1つの欠点は、必要な#ifdefステートメントを追加するために、コンパイラライブラリヘッダーファイルおよびその他のサードパーティファイルを変更する必要があることです。

別の一般的な方法は、プロジェクトヘッダーファイルの前に、すべてのシステムヘッダーファイルを本文ファイルに含めることです。一部のプロジェクトヘッダーファイルは、システムヘッダーの定義を使用するため、またはシステム定義をオーバーライドするために、システムヘッダーファイルに依存する可能性があるため、この標準はこの慣行に従いません。このようなプロジェクトヘッダーファイルには、システムヘッダーの#includeステートメントが含まれている必要があります。ボディに最初に含まれている場合、コンパイラはこれをチェックしません。

インターネットアーカイブ経由で利用可能なGSFC標準2012-12-10

情報 礼儀 Eric S. Bullington

参照されているNASA Cコーディング標準は、インターネットアーカイブからアクセスおよびダウンロードできます。

http://web.archive.org/web/20090412090730/http://software.gsfc.nasa.gov/assetsbytype.cfm?TypeAsset=Standard

シーケンス

質問はまた尋ねます:

はいの場合、#include#ifndefの間、または#defineの後に(#define行)を挿入する必要もあります。

答えは正しいメカニズムを示しています—ネストされたインクルードなどは#defineの後にあるべきです(そして#defineはヘッダーの2番目の非コメント行でなければなりません)—しかし、それはなぜ説明しませんそのとおりです。

#include#ifndefの間に#defineを配置するとどうなるかを考えてください。他のヘッダー自体に、おそらく間接的に#include "magicsort.h"などのさまざまなヘッダーが含まれているとします。 magicsort.hの2番目のインクルードが#define MAGICSORT_H_INCLUDEDの前に発生する場合、ヘッダーは、それが定義するタイプが定義される前に2回インクルードされます。そのため、C89およびC99では、typedef型名が誤って再定義されます(C2011では同じ型に再定義できます)、andを取得しますファイルを複数回処理するオーバーヘッド。ヘッダーガードの目的をそもそも無効にします。これは、#defineが2行目であり、#endifの直前に書き込まれない理由でもあります。与えられた式は信頼できます:

#ifndef HEADERGUARDMACRO
#define HEADERGUARDMACRO

...original content of header — other #include lines, etc...

#endif /* HEADERGUARDMACRO */
95

インクルードファイルで必要な場合にのみ、インクルードファイルに#includesを配置することをお勧めします。特定のインクルードファイルの定義が.cファイルでのみ使用される場合は、.cファイルにのみインクルードします。

あなたの場合、私は#ifdef /#endifの間のインクルードファイルにそれを含めます。

これにより、依存関係が最小限に抑えられるため、インクルードファイルが変更されても、インクルードを必要としないファイルを再コンパイルする必要がなくなります。

20

必要なインクルードファイルがこのインクルードの前に含まれていることを確認するために、次の構成を使用します。ソースファイルのみにすべてのヘッダーファイルを含めます。

#ifndef INCLUDE_FILE_H
 #error "#include INCLUDE.h" must appear in source files before "#include THISFILE.h"
#endif
0
Thomas Weber

コンパイル中に、プリプロセッサは#includeディレクティブを指定されたファイルコンテンツに置き換えるだけです。無限ループを防ぐために使用する必要があります

#ifndef SOMEIDENTIFIER
#define SOMEIDENTIFIER
....header file body........
#endif

ファイルに含まれていた別のヘッダーにヘッダーが含まれていた場合、ファイルに再帰的に含まれるため、再度明示的に含める必要はありません。

0
Alex

はい、それは必要です。そうでない場合、コンパイラは「認識していない」コードをコンパイルしようとすると文句を言います。 #includeは、コンパイルを成功させるために宣言、構造などを取得するようコンパイラーに指示するヒント/ナッジ/エルボであると考えてください。 jldupontが指摘した#ifdef /#endifヘッダートリックは、コードのコンパイルを高速化することです。

ここに示すように、C++コンパイラがあり、プレーンCコードをコンパイルしているインスタンスで使用されます トリックの例を次に示します。

#ifndef __MY_HEADER_H __ 
#define __MY_HEADER_H __ 
 
#ifdef __cplusplus 
 extern "C" {
#endif 
 
 
/*構造体、宣言などのCコードはここに*/
 
#ifdef __cplusplus 
} 
# endif 
 
#endif/* __MY_HEADER_H__ */

現在、これが複数回含まれていた場合、コンパイラはシンボル__MY_HEADER_H__が一度定義されると、コンパイル時間が短縮されます。 上記の例のシンボルcplusplusに注意してください。これは、Cコードが存在する場合にC++コンパイルに対処する通常の標準的な方法です。

これを示すために上記を含めました(ポスターの元の質問とは実際には関係ありませんが)。これがお役に立てば幸いです、よろしく、トム。

PS:C/C++の初心者には便利だと思っていたので、誰かにこれを投票させてすみません。コメント/批評などは大歓迎です。

0
t0mm13b

ヘッダーからヘッダーを含める必要があります。cにヘッダーを含める必要はありません。インクルードは、不必要に複数回インクルードされないように、#defineの後に配置する必要があります。例えば:

/* myHeader.h */
#ifndef MY_HEADER_H
#define MY_HEADER_H

#include <glib.h>

struct S
{
    gchar c;
};

#endif /* MY_HEADER_H */

そして

/* myCode.c */
#include "myHeader.h"

void myFunction()
{
    struct S s;
    /* really exciting code goes here */
}
0
danio

通常、ライブラリ開発者は#ifndef /#define/#endif "trick"を使用して複数のインクルードからインクルードを保護するため、ユーザーはそれを行う必要がありません。

もちろん、チェックする必要があります...しかし、とにかくコンパイラーはある時点であなたに教えてくれます;-)それはコンパイルサイクルを遅くするので、とにかく複数のインクルードをチェックすることは良い習慣です。

0
jldupont

プロジェクトの1つの共通ヘッダーファイルにすべての外部ヘッダーを含めるだけです。 global.hそしてすべてのcファイルにそれを含めます:

次のようになります。

#ifndef GLOBAL_GUARD
#define GLOBAL_GUARD

#include <glib.h>
/*...*/
typedef int  YOUR_INT_TYPE;
typedef char YOUR_CHAR_TYPE;
/*...*/
#endif

このファイルは、インクルードガードを使用して、複数のインクルード、不正な複数の定義などを回避します。

0
psihodelia