C ヘッダー ファイルのループ

ここでの問題は、インクルード ガードの欠落ではなく、2 つの構造体の定義で互いが必要であるという事実だと思います。つまり、型定義のハンと卵の問題です。

C または C++ でこれらを解決する方法は、型の前方宣言を行うことです。要素が何らかの構造体であることをコンパイラに伝えると、コンパイラはそれへのポインタを生成できます。

tree.h 内:

// tell the compiler that element is a structure typedef:
typedef struct element_ element;

typedef struct tree_ tree;
struct tree_
{
    tree *first_child;
    tree *next_sibling;
    int tag;

    // now you can declare pointers to the structure.
    element *obj;
};

そうすれば、tree.h 内に element.h を含める必要がなくなります。

また、ヘッダー ファイルの周囲にもインクルード ガードを配置する必要があります。


ここで重要なことは、要素はツリーへのポインタを保持するだけなので、ツリーの構造を知る必要がないということです。木も同じ。それぞれが知る必要があるのは、その中に何が含まれているかではなく、関連する名前の型が存在することだけです。

したがって、tree.h では、代わりに:

#include "element.h"

する:

typedef struct element_ element;

これは型「element」と「struct element_」を「宣言」しますが (それらが存在することを示します)、それらを「定義」しません (それらが何であるかを示します)。 blah へのポインターを格納するために必要なのは、blah が定義されていることではなく、blah が宣言されていることだけです。それを参照したい場合 (メンバーを読み取る場合など) にのみ、定義が必要です。 ".c" ファイルのコードはそれを行う必要がありますが、この場合、ヘッダーは必要ありません。

一部の人々は、ヘッダーのクラスター内のすべてのタイプを前方宣言する単一のヘッダー ファイルを作成し、各ヘッダーにそれを含めます。実際に必要なタイプを特定するのではありません。それは本質的なことでも、完全にばかげたことでもありません。

インクルード ガードに関する回答は間違っています。一般的には良いアイデアです。それらについて読んで理解する必要がありますが、特に問題を解決するわけではありません。


正解は、インクルード ガードを使用し、前方宣言を使用することです。

ガードを含める

/* begin foo.h */
#ifndef _FOO_H
#define _FOO_H

// Your code here

#endif
/* end foo.h */

Visual C++ は #pragma once もサポートしています。非標準のプリプロセッサ ディレクティブです。コンパイラの移植性と引き換えに、プリプロセッサ名の衝突の可能性を減らし、読みやすさを向上させます。

前方宣言

構造体を前方宣言します。構造体またはクラスのメンバーが明示的に必要でない場合は、ヘッダー ファイルの先頭でそれらの存在を宣言できます。

struct tree;    /* element.h */
struct element; /* tree.h    */