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


115

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

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

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

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

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

万一I typedefそれ?もしそうなら、どうですか?


構造体の定義は有効ではないことに注意してください。Cの場合、の閉じかっこの後にセミコロンが必要ですが、struct b構造体aは未使用の型を宣言しています(おそらくk、内側の閉じかっこの後にメンバー名を定義する必要があります。セミコロンの前
ジョナサンレフラー2014

回答:


140

この構造が他のファイル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 */
}

構造体の名前を維持することが重要です。書き込み:

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++ explicit declaration by the user
struct MyStruct
{
   /* etc. */
} ;
// C++ standard then implicitly adds the following line
typedef MyStruct MyStruct;

Cに戻ると、両方の使用法(別々の名前と同じ名前)を見てきましたが、私が知っている欠点はありません。そのため、同じ名前を使用すると、構造体やその他のシンボルにCの個別の「名前空間」を使用しない場合、読み取りが簡単になります


2
「C99を使用している場合はコメントしない」というコメントを保証するC99標準のセクションについてコメントまたは指摘できますか?
ジョナサンレフラー

あなたが正しい。私は最近C99をテストしましたが、C ++の方法を使用したときに型なしの構造体が認識されないことに驚いていました。コンパイラオプションを検索したところ、すべての標準ドキュメントで手に入れることができましたが、それを説明できるものは何も見つかりませんでした。それは可能だと信じていました...
paercebal 2008年

2
...とにかく、発言ありがとうございます。私は今それを修正しました。
paercebal 2008年

structタグと名前に異なる名前を使用する必要はありませんtypedef。Cはstructタグに異なる名前空間を使用するMyStructため、両方に使用できます。
クレイグバーンズ

1
@CraigBarnesその通りですが、コードを読んだだけでそれを明確にしたかったのです。同じ名前を付けた場合、同じ "宣言"に* twice "という名前を書く必要があるため、Cの初心者を混乱させる可能性があります。コメントを記載したメモを追加します。ありがとう!
paercebal

34

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

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

extern struct a myAValue;

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

struct a myAValue;

これを忘れたり、2つのソースファイルで誤って定義したりすると、リンカによって通知されます。


リンカーエラーが発生する場合と発生しない場合があります。Cでは、リンケージモデルは「共通の」定義を許可しているため、イニシャライザのない(そしておそらく同じイニシャライザを持つ)複数の定義が機能する可能性があります。これは「共通の拡張機能」です。一部のコンパイラーは暫定(欠落)定義もサポートしていると私は信じています。
ジョナサンレフラー、

次に、1つのソースファイルで実際の変数を定義します。...または2つのソースファイルで誤って定義します... :)
Johannes Schaub-litb '25年

宣言/定義の問題に使用した1つの手法は、ヘッダーの上部で条件付きで定義するか、何も定義GLOBALexternないで、変数をとして宣言することですGLOBAL struct a myAValue;。ほとんどのソースファイルから、#define GLOBAL extern使用するバージョン(変数の宣言)を調整し、1つのソースファイルから空の定義を使用して変数を定義します。
TripeHound 2014年

Cではtypedef名と同じ構造名を使用できますが、C ++では使用できません。
xuhdev 2014年

6

ああ:

#ifndef A_H
#define A_H

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

#endif

これで、この構造を使用するファイルにahを含める必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.