75

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

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

はいの場合、との間に配置する必要が#ifndefあり#defineます#defineか?

4

9 に答える 9

116

NASAのゴダードスペースフライトセンター(GSFC)のCのヘッダーに関する規則では、ソースファイルに唯一のヘッダーとしてヘッダーを含めることができなければならず、そのヘッダーによって提供される機能を使用するコードがコンパイルされます。

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

  • 自己完結型—必要なすべてのタイプは、必要に応じて関連するヘッダーを含めることによって定義されます。
  • べき等—複数回含まれていても、コンパイルは損益分岐点になりません。
  • 最小—ヘッダーで定義された機能にアクセスするためにヘッダーを使用するコードで必要とされないものは定義されません。

このルールの利点は、誰かがヘッダーを使用する必要がある場合、他のどのヘッダーも含める必要があるかを判断するのに苦労する必要がないことです。ヘッダーが必要なすべてを提供することを知っています。

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

実装

このルールは、ヘッダーが' FILE *'や''などのタイプを使用する場合size_t、適切な他のヘッダー(<stdio.h>または<stddef.h>たとえば)が含まれていることを確認する必要があることを意味します。当然のことながら、忘れられがちなのは、パッケージを使用するためにパッケージのユーザーが必要としない他のヘッダーをヘッダーに含めてはならないということです。つまり、ヘッダーは最小限にする必要があります。

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

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

したがって、MagicSortがあるとします。

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')にはそれら自体を含める必要があり、ヘッダーに依存してそれらを含めることはできません。ヘッダーには、ソフトウェアのユーザーが必要とするものだけを含める必要があります。実装者が必要とするものではありません。

構成ヘッダー

コードで構成ヘッダー(GNUAutoconfや生成された「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つだけになります。クライアントユニットは、ユニットヘッダーで定義された関数のみを呼び出すことができます。本体で定義されているがヘッダーで宣言されていない関数を呼び出すことはできません。クライアントユニットは、本文では宣言されているがヘッダーでは宣言されていない変数にアクセスできません。

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

スタンドアロンヘッダーファイルには、関連付けられた本文はありません。たとえば、common typesヘッダーは関数を宣言しないため、本文は必要ありません。

ユニットに複数の本文ファイルがある理由:

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

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

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

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

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

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

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

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

インターネットアーカイブ2012-12-10から入手可能なGSFC標準

情報提供: Eric S. Bullington :

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

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"間接的にさえ、さまざまなヘッダーが含まれているとします。の2番目の包含がのmagicsort.h前に発生した場合#define MAGICSORT_H_INCLUDED、ヘッダーは、それが定義するタイプが定義される前に2回目に包含されます。そのため、C89およびC99では、typedefタイプ名が誤って再定義され(C2011では同じタイプに再定義できます)、ファイル複数回処理するオーバーヘッドが発生し、最初のヘッダーガードの目的が損なわれます。場所。#defineこれは、が2行目であり、の直前に書き込まれない理由でもあり#endifます。与えられた式は信頼できます:

#ifndef HEADERGUARDMACRO
#define HEADERGUARDMACRO

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

#endif /* HEADERGUARDMACRO */
于 2009-11-26T16:43:25.060 に答える
22

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

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

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

于 2009-11-26T16:10:41.337 に答える
0

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

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

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

于 2009-11-26T16:11:19.057 に答える
0

ヘッダーからヘッダーを含める必要があり、.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 */
}
于 2009-11-26T16:23:13.447 に答える
0

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

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

#ifndef __MY_HEADER_H__
#定義__MY_HEADER_H__

#ifdef __cplusplus
extern "C" {
#endif


/* 構造体、宣言などの C コードをここに記述 */

#ifdef __cplusplus
}
#endif

#endif /* __MY_HEADER_H__ */

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

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

PS: C/C++ の初心者にとって役立つ情報だと思っていたので、誰かに反対票を投じさせて申し訳ありません。コメント/批評などを残してください。大歓迎です。

于 2009-11-26T16:13:32.073 に答える
0

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

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

于 2009-11-26T15:57:19.907 に答える
0

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

#ifndef INCLUDE_FILE_H
 #error "#include INCLUDE.h" must appear in source files before "#include THISFILE.h"
#endif
于 2009-11-26T17:04:46.853 に答える
-1

私が通常行うことは、必要なすべての依存関係を正しい順序で含む単一のインクルード ファイルを作成することです。だから私は持っているかもしれません:

#ifndef _PROJECT_H_
#define _PROJECT_H_
#include <someDependency.h>
#include "my_project_types.h"
#include "my_project_implementation_prototypes.h"
#endif

すべて project.h にあります。これで、project.h は注文要件なしでどこにでも含めることができますが、依存関係、型、および API 関数のプロトタイプを別のヘッダーに含めるという余裕があります。

于 2009-11-26T18:11:02.290 に答える
-2

プロジェクトの1 つの共通ヘッダー ファイル( global.hなど)にすべての外部ヘッダーをインクルードし、それをすべての c ファイルにインクルードします。

次のようになります。

#ifndef GLOBAL_GUARD
#define GLOBAL_GUARD

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

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

于 2009-11-26T16:04:29.237 に答える