web-dev-qa-db-ja.com

.hファイルと.cを#ifdefヘッダーガードでリンクする

.hファイルと.cファイルのリンクに問題があります。また、この問題に関するいくつかのスレッドを読みましたが、それらはすべて漠然としていて、まだ概念を完全に理解できていません。リンクに関する多くの問題があります。 、私がbcとbhを持っていると言います、そして私はbhをacとbcの両方に含めるかどうか混乱していますcuz bc自体がbhで定義された構造を知る必要がある、私はいくつかの関数を持っていますそのプロトタイプはbhであり、bcで定義されており、bhの構造も使用します。bcは、bcの関数を使用するacへのインターフェイスに似ているため、bc cuzにbhを含めません。例

b.hファイル

typedef struct{
int x, y;
}myStruct;

void funct1(myStruct);
void funct2(myStruct);

b.cファイル

void funct1(myStruct x)
{
    //do something
}

void funct2(myStruct y)
{
     //do something
} 

a.cファイル

#include "b.h"

int main()
{
myStruct x;
  funct1(x);
  funct2(y);
return 0;
}

Cygwinでコマンドを実行:gcc b.c a.c -g

混乱する部分ですが、b.cをコンパイルすると、b.hの構造とプロトタイプを検出できないというリンクエラーが発生します。私が知っているすべてのことは、b.hがa.cからb.cをリンクするために使用されることですが、両方の.cがコンパイルされると、b.cがその構造とプロトタイプを見つけることができないようです。

なぜbhをbcに含めなかったのですか?Answer:Cuzは私が知っているとおり、bhはすでにacに含まれており、bcに再び含めたときは、ダブルインクルージョンを行う<---これまでに学んだことであり、#ifdefがあることはわかっていますが、機能しないようです。まだ使い方がわからない場合は、お気軽にご相談ください。この。

どうしたらいいかわからない場合は、遠慮なく教えてください。

#ifdefディレクティブがありますが、これを行う方法がわからないようです。

注:上記のすべてのコードを想定ISスペルが間違っている単語がある場合は、構文的に正しく無視してください。hと.cの間に挿入した後のみです。

13
lemoncodes

確かに#include b.hb.cする必要があります。各ファイルは、リンカーが引き継ぐ前に個別にコンパイルされます。したがって、b.cはそれ自体でコンパイルされ、それを含めない限りb.hの内容を認識しないため、b.hをa.cに含めても問題ありません。

これが#includeガードの例です

// some_header_file.h
#ifndef SOME_HEADER_FILE_H
#define SOME_HEADER_FILE_H
// your code
#endif

Some_header_file.hがどこかに含まれている場合、SOME_HEADER_FILE_Hが定義されていると、#ifndef#endifの間のすべてが無視されます。これは、コンパイルユニットに初めて含まれるときに発生します。

プロジェクト内での一意性を確保するために、ファイル名の後に#defineを付けるのが一般的な方法です。他のコードとの衝突のリスクを減らすために、プロジェクトまたは名前空間の名前も前に付けたいと思います。

注:上記のinclude guardを使用しても、同じヘッダーファイルをプロジェクト内に複数回含めることができます。同じコンパイルユニット内に2回含めることはできません。これは次のように示されます。

// header1.h
#ifndef HEADER_H
#define HEADER_H
int test1 = 1;
#endif

// header2.h
#ifndef HEADER_H
#define HEADER_H
int test2 = 2;
#endif

次に、上記の2つのファイルを含めようとするとどうなるかを見てみましょう。単一のコンパイル単位で:

// a.cpp
#include "header1.h"
#include "header2.h"
#include <iostream>
int main()
{
   std::cout << test1;
   std::cout << test2;
};

Test2が定義されていないため、これはコンパイラエラーを生成します。header2.hでは、HEADER_Hが含まれる時点ですでに定義されているため、これは無視されます。ここで、各ヘッダーを別々のコンパイル単位に含めます。

// a.cpp
#include "header2.h"
int getTest2()
{
   return test2;
};

// b.cpp
#include "header1.h"
#include <iostream>
int getTest2(); // forward declaration
int main()
{
   std::cout << test1;
   std::cout << getTest2();
};

どちらもHEADER_Hを定義する2つのファイルが含まれていますが、正常にコンパイルされ、予期される出力(1および2)が生成されます。

27
JBentley

b.hで定義されている構造を使用するすべてのファイルにb.hを含める必要があります。したがって、両方のファイルに#include <b.h>を含める必要があります。 b.hが数回読み込まれるのを回避するには、ディレクティブ#ifdefが必要です。あなたの場合:

b.h

#ifndef B_H
#define B_H

typedef struct{
    int x, y;
}myStruct;

void funct1(myStruct);
void funct2(myStruct);

#endif

およびb.c:

#include "b.h"

void funct1(myStruct x)
{
    //do something
}

void funct2(myStruct y)
{
     //do something
} 

適切なコーディングでは、b.hをb.cに含めます。

動作するはずのヘッダーガードを次に示します。

#ifndef B_H_INCLUDED
#define B_H_INCLUDED
//header file
#endif

宣言はコメントのある場所に置き、必要なすべての場所に含めます。

編集私が理解している方法では、gccは最初にb.cをコンパイルします。これは、a.cがb.cに依存しているためです。しかし、それが最初にb.cをコンパイルするとき、b.h まだ含まれていませんです。

1
BenjiWiebe

必要がある #include b.cのb.h。これは単にa.cのインターフェイスではなく、b.cは独自のコードの同じ定義も知っている必要があります。 b.cにb.hを含めない理由は間違っています。各.cファイルは、他のすべての.cファイルとは別にコンパイルされます。コンパイラがa.cで完了すると、b.cで最初からやり直します。 a.cにb.hが含まれていても問題ありません。b.cにはa.cが存在するという概念さえないためです。ヘッダーガードの目的は、特定の.cファイルのコンパイル中に.hファイルが複数回インクルードされた場合に、.hファイルが繰り返し処理されるのを防ぐことです。ガードがないと、宣言が複数回コンパイルされ、既存のシンボルの複数の宣言に関するエラーが発生します。

0
Remy Lebeau