web-dev-qa-db-ja.com

cの複数のファイルで使用されるヘッダーの構造を宣言する方法は?

構造体を持つsource.cファイルがある場合:

struct a { 
    int i;
    struct b {
        int j;
    }
};

この構造体を別のファイル(つまりfunc.c)で使用するにはどうすればよいですか?

新しいヘッダーファイルを作成し、そこで構造体を宣言して、そのヘッダーをfunc.cに含める必要がありますか?

または、ヘッダーファイルで構造体全体を定義し、source.cfunc.cの両方に含める必要がありますか?両方のファイルでextern構造体を宣言するにはどうすればよいですか?

typedefすべきですか?もしそうなら、どのように?

110
Manoj

この構造が他のファイルfunc.cによって使用される場合、その方法は?

ファイル(つまりfunc.cファイル)でタイプを使用する場合、そのタイプは表示されている必要があります。最悪の方法は、必要な各ソースファイルにコピーペーストすることです。

正しい方法は、ヘッダーファイルに配置し、必要に応じてこのヘッダーファイルをインクルードすることです。

新しいヘッダーファイルを開き、そこで構造を宣言し、そのヘッダーをfunc.cに含めますか?

これは、コードを高度にモジュール化するため、私がより気に入っているソリューションです。私はあなたの構造体を次のようにコーディングします:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME

struct a
{ 
    int i;
    struct b
    {
        int j;
    }
};

#endif

このヘッダーを使用して、この構造を使用する関数(「インターフェイス」の「意味的に」の部分である関数)を配置します。

そして、通常、ファイルに構造名の後に名前を付け、その名前を再度使用して、定義されているヘッダーガードを選択します。

構造体へのポインタを使用して関数を宣言する必要がある場合、完全な構造体定義は必要ありません。次のような単純な前方宣言:

struct a ;

十分であり、カップリングを減少させます。

または、ヘッダーファイルで構造全体を定義し、source.cとfunc.cの両方に含めることができますか?

これは別の方法で、多少簡単ですが、モジュール性は低くなります。機能するのに構造だけが必要なコードには、すべてのタイプを含める必要があります。

C++では、これは興味深い複雑化につながる可能性がありますが、これはトピック外(C++タグなし)なので、詳しく説明しません。

次に、両方のファイルでその構造をexternとして宣言する方法。 ?

おそらく、ポイントはわかりませんが、Greg Hewgillの投稿で非常に良い答えがあります cの複数のファイルで使用されるヘッダーの構造を宣言する方法

それからtypedefしますか?

  • C++を使用している場合は、使用しないでください。
  • Cを使用している場合は、そうする必要があります。

その理由は、C構造体の管理が苦痛になる可能性があることです。使用するすべての場所でstructキーワードを宣言する必要があります。

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

void doSomething(struct MyStruct * p) /* parameter */
{
   struct MyStruct a ; /* variable */
   /* etc */
}

Typedefを使用すると、structキーワードなしで記述できます。

struct MyStructTag ; /* Forward declaration */

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

void doSomething(MyStruct * p) /* parameter */
{
   MyStruct a ; /* variable */
   /* etc */
}

importantまだ構造体の名前を保持しています。書き込み:

typedef struct
{
   /* etc. */
} MyStruct ;

typedefされた名前で匿名の構造体を作成するだけで、前方宣言することはできません。そのため、次の形式に従ってください。

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

したがって、structキーワードの追加を回避したいすべての場所でMyStructを使用でき、typedefが機能しない場合(つまり前方宣言)でもMyStructTagを使用できます。

編集:

Jonathan Leffler で正しく述べられているように、C99構造体宣言に関する誤った仮定を修正しました。

2018-06-01を編集:

Craig Barnes コメントで、構造体の「タグ」名とその「typedef」名に別々の名前を付ける必要はないことを思い出させてくれます。

実際、上記のコードは次のように書くこともできます。

typedef struct MyStruct
{
   /* etc. */
} MyStruct ;

IIRC、これは実際には、C++とCとの互換性を保つために、舞台裏でC++がより単純な構造体宣言で行うことです。

// C++ explicit declaration by the user
struct MyStruct
{
   /* etc. */
} ;
// C++ standard then implicitly adds the following line
typedef MyStruct MyStruct;

Cに戻って、両方の使用法(別々の名前と同じ名前)を見てきましたが、どれも私が知っている欠点はないので、同じ名前を使用すると 構造体およびその他の記号

129
paercebal

複数のソースファイルで使用される構造定義の場合は、必ずヘッダーファイルに配置する必要があります。次に、そのヘッダーファイルを、構造が必要なソースファイルに含めます。

extern宣言は構造定義には使用されませんが、代わりに変数宣言(つまり、定義した構造タイプを持つ一部のデータ値)に使用されます。同じ変数を複数のソースファイルで使用する場合は、次のようなヘッダーファイルでexternとして宣言します。

extern struct a myAValue;

次に、oneソースファイルで、実際の変数を定義します。

struct a myAValue;

これを行うのを忘れた場合、または誤って2つのソースファイルで定義した場合、リンカーはこれを通知します。

33
Greg Hewgill

a.h:

#ifndef A_H
#define A_H

struct a { 
    int i;
    struct b {
        int j;
    }
};

#endif

これで、この構造を使用するファイルにa.hを含めるだけです。

5
fmsf