C 头文件循环

发布于 2024-07-07 09:36:17 字数 505 浏览 10 评论 0原文

我有几个头文件,可以归结为:

tree.h:

#include "element.h"

typedef struct tree_
{
    struct *tree_ first_child;
    struct *tree_ next_sibling;
    int tag;
    element *obj;
    ....
} tree;

和 element.h:

#include "tree.h"

typedef struct element_
{
    tree *tree_parent;
    char *name;
    ...
} element;

问题是它们都互相引用,因此树需要包含元素,而元素需要包含树。

这是行不通的,因为要定义“树”结构,元素结构必须已知,而要定义元素结构,必须知道树结构。

如何解决这些类型的循环(我认为这可能与“前向声明”有关?)?

I have a couple of header files, which boil down to:

tree.h:

#include "element.h"

typedef struct tree_
{
    struct *tree_ first_child;
    struct *tree_ next_sibling;
    int tag;
    element *obj;
    ....
} tree;

and element.h:

#include "tree.h"

typedef struct element_
{
    tree *tree_parent;
    char *name;
    ...
} element;

The problem is that they both reference each other, so tree needs element to be included, and element needs tree to be included.

This doesn't work because to define the 'tree' structure, the element structure must be already known, but to define the element structure, the tree structure must be known.

How to resolve these types of loops (I think this may have something to do with 'forward declaration'?)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

猫弦 2024-07-14 09:36:17

我认为这里的问题不是缺少包含保护,而是两个结构在定义中相互需要的事实。 所以这是一个类型定义 hann 和 Egg 的问题。

在 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 了。

您还应该在头文件周围放置包含保护。

I think the problem here is not the missing include guard but the fact that the two structures need each other in their definition. So it's a type define hann and egg problem.

The way to solve these in C or C++ is to do forward declarations on the type. If you tell the compiler that element is a structure of some sort, the compiler is able to generate a pointer to it.

E.g.

Inside 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;
};

That way you don't have to include element.h inside tree.h anymore.

You should also put include-guards around your header-files as well.

幸福丶如此 2024-07-14 09:36:17

这里重要的观察是元素不需要知道树的结构,因为它只保存指向树的指针。 对于树来说也是如此。 每个人都需要知道的是存在一个具有相关名称的类型,而不是其中的内容。

因此,在 tree.h 中,而不是:

#include "element.h"

do:

typedef struct element_ element;

这“声明”类型“element”和“struct element_”(表示它们存在),但不“定义”它们(表示它们是什么)。 存储指向 blah 的指针所需要做的就是声明 blah,而不是定义它。 仅当您想要遵循它(例如读取成员)时才需要定义。 “.c”文件中的代码需要执行此操作,但在本例中,您的标头不需要执行此操作。

有些人创建一个头文件,该文件前向声明头簇中的所有类型,然后每个头都包含该类型,而不是确定它真正需要哪些类型。 这既不是必要的,也不是完全愚蠢的。

关于包含警卫的答案是错误的 - 一般来说,它们是一个好主意,您应该阅读它们并为自己准备一些,但它们并不能特别解决您的问题。

Crucial observation here is that the element doesn't need to know the structure of tree, since it only holds a pointer to it. The same for the tree. All each needs to know is that there exists a type with the relevant name, not what's in it.

So in tree.h, instead of:

#include "element.h"

do:

typedef struct element_ element;

This "declares" the types "element" and "struct element_" (says they exist), but doesn't "define" them (say what they are). All you need to store a pointer-to-blah is that blah is declared, not that it is defined. Only if you want to deference it (for example to read the members) do you need the definition. Code in your ".c" file needs to do that, but in this case your headers don't.

Some people create a single header file which forward-declares all the types in a cluster of headers, and then each header includes that, instead of working out which types it really needs. That's neither essential nor completely stupid.

The answers about include guards are wrong - they're a good idea in general, and you should read about them and get yourself some, but they don't solve your problem in particular.

那支青花 2024-07-14 09:36:17

正确的答案是使用包含防护,并使用前向声明。

Include Guards

/* 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    */

The correct answer is to use include guards, and to use forward declarations.

Include Guards

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

// Your code here

#endif
/* end foo.h */

Visual C++ also supports #pragma once. It is a non standard preprocessor directive. In exchange for compiler portability, you reduce the possibility of preprocessor name collisions and increase readability.

Forward Declarations

Forward declare your structs. If the members of a struct or class are not explicitly needed, you can declare their existence at the beginning of a header file.

struct tree;    /* element.h */
struct element; /* tree.h    */
莫言歌 2024-07-14 09:36:17

了解前向声明

IE。


// tree.h:
#ifndef TREE_H
#define TREE_H
struct element;
struct tree
{
    struct element *obj;
    ....
};

#endif

// element.h:
#ifndef ELEMENT_H
#define ELEMENT_H
struct tree;
struct element
{
    struct tree *tree_parent;
    ...
};
#endif

Read about forward declarations.

ie.


// tree.h:
#ifndef TREE_H
#define TREE_H
struct element;
struct tree
{
    struct element *obj;
    ....
};

#endif

// element.h:
#ifndef ELEMENT_H
#define ELEMENT_H
struct tree;
struct element
{
    struct tree *tree_parent;
    ...
};
#endif
甜味超标? 2024-07-14 09:36:17

包含防护很有用,但不能解决发布者的问题,即对两个数据结构的递归依赖。

这里的解决方案是将树和/或元素声明为指向头文件中结构的指针,因此您不需要包含 .h

类似的内容:

struct element_;
typedef struct element_ element;

在 tree.h 的顶部应该足以消除包含的需要element.h

使用这样的部分声明,您只能使用元素指针执行不需要编译器了解有关布局的任何信息的操作。

Include guards are useful, but don't address the poster's problem which is the recursive dependency on two data structures.

The solution here is to declare tree and/or element as pointers to structs within the header file, so you don't need to include the .h

Something like:

struct element_;
typedef struct element_ element;

At the top of tree.h should be enough to remove the need to include element.h

With a partial declaration like this you can only do things with element pointers that don't require the compiler to know anything about the layout.

茶花眉 2024-07-14 09:36:17

恕我直言,最好的方法是避免此类循环,因为它们是应该避免的物理耦合的标志。

例如(据我所知)“面向对象的设计启发式”目的是避免包含防护,因为它们仅屏蔽循环(物理)依赖性。

另一种方法是预先声明结构,如下所示:
<代码>

element.h:
struct tree_;
struct element_
  {
    struct tree_ *tree_parent;
    char *name;
  };

树.h: 结构元素_; 结构树_ { 结构树_*first_child; 结构树_* next_sibling; int 标签; 结构元素_*obj; };

IMHO the best way is to avoid such loops because they are a sign of physical couping that should be avoided.

For example (as far as I remember) "Object-Oriented Design Heuristics" purpose to avoid Include Guards because they only mask the cyclic (physical) dependency.

An other approach is to predeclare the structs like this:

element.h:
struct tree_;
struct element_
  {
    struct tree_ *tree_parent;
    char *name;
  };

tree.h: struct element_; struct tree_ { struct tree_* first_child; struct tree_* next_sibling; int tag; struct element_ *obj; };

触ぅ动初心 2024-07-14 09:36:17

前向声明是一种可以保证稍后定义的结构类型的方法。

Forward declaratio is the way with which you can guarantee that there will be a tyoe of structure which will be defined later on.

明天过后 2024-07-14 09:36:17

我不喜欢前向声明,因为它们是多余的并且有错误。 如果您希望所有声明都位于同一位置,那么您应该使用包含和带有包含防护的头文件。

您应该将 include 视为复制粘贴,当 c 预处理器找到 #include 行时,会将 myheader.h 的整个内容放在找到 #include 行的同一位置。

好吧,如果您编写 include 保护,则 myheader.h 的代码将仅在找到第一个 #include 的地方粘贴一次。

如果您的程序使用多个目标文件进行编译并且问题仍然存在,那么您应该在目标文件之间使用前向声明(就像使用 extern),以便仅保留所有目标文件的类型声明(编译器混合同一个表中的所有声明,并且标识符必须是独一无二的)。

I don't like forward declarations cause they are redundant and buggy. If you want all your declarations in the same place then you should use includes and header files with include guards.

You should think about includes as a copy-paste, when the c preprocesor finds an #include line just places the entire content of myheader.h in the same location where #include line was found.

Well, if you write include guards the myheader.h's code will be pasted only one time where the first #include was found.

If your program compiles with several object files and problem persists then you should use forward declarations between object files (it's like using extern) in order to keep only a type declaration to all object files (compiler mixes all declarations in the same table and identifiers must be unique).

暖阳 2024-07-14 09:36:17

一个简单的解决方案是不使用单独的头文件。 毕竟,如果它们相互依赖,那么您永远不会只使用其中一个而不使用另一个,那么为什么要将它们分开呢? 您可以拥有单独的 .c 文件,它们都使用相同的标头,但提供更集中的功能。

我知道这并不能回答如何正确使用所有奇特的东西的问题,但当我寻找类似问题的快速解决方案时,我发现它很有帮助。

A simple solution is to just not have separate header files. After all, if they're dependent on each other you're never going to use one without the other, so why separate them? You can have separate .c files that both use the same header but provide the more focused functionality.

I know this doesn't answer the question of how to use all the fancy stuff correctly, but I found it helpful when I was looking for a quick fix to a similar problem.

荭秂 2024-07-14 09:36:17

这里的很多答案都提到了“包括守卫”和“前向声明”,但他们都没有真正意图解决OP当前面临的问题。 第三个“.h”文件绝对不是答案。 如果使用得当,“包含防护”可以打破“#include循环”并最终导致更清晰的项目结构。 如果您已经有两个头文件,为什么还要为 typedef 创建另一个头文件呢? 你的头文件应该是这样的:

/* a.h - dependency of b.h */
#ifndef _A_H
#define _A_H

#include "b.h"

typedef struct a_p {
    b_t *b;
} a_t;

#endif // _A_H
/* b.h - dependency of a.h */
#ifndef _B_H
#define _B_H

typedef struct b_p b_t;

/** 
 * !!!
 * to avoid recursion, only include "a.h" 
 * when "a.h" isn't included before
 */
#ifndef _A_H
    #include "a.h"
    typedef struct b_p {
        a_t a;
    } b_t;
#endif

#endif // _B_H

要使用这两个头文件,你只需要 include 一个,即无条件包含另一个的头文件(在本例中,ah)。 但如果您愿意,您也可以包含“bh”。 但无论如何,它不会产生任何影响(由于前向声明)。

#include "a.h"

int main() {
    a_t aigh;

    return 0;
}

瞧! 就是这个! 没有额外的include什么都没有。 我们有他们!

So many answers here has mentioned "include guards" and "forward declaration" but none of them really has the intention to solve the issue the OP is currently facing. A third ".h" file is definitely not the answer. "Include guards" if used properly can break the "#include loop" and eventually lead to cleaner project structure. Why even bother creating another header file just for the typedefs if you already got two?? Your header files should be like this:

/* a.h - dependency of b.h */
#ifndef _A_H
#define _A_H

#include "b.h"

typedef struct a_p {
    b_t *b;
} a_t;

#endif // _A_H
/* b.h - dependency of a.h */
#ifndef _B_H
#define _B_H

typedef struct b_p b_t;

/** 
 * !!!
 * to avoid recursion, only include "a.h" 
 * when "a.h" isn't included before
 */
#ifndef _A_H
    #include "a.h"
    typedef struct b_p {
        a_t a;
    } b_t;
#endif

#endif // _B_H

To use both of the header files you only need to include one, that is, the one that also unconditionally includes another (in this case, a.h). But if you wanna, you may also include "b.h" as well. But it's not going to make any difference (due to forward declaration) anyway.

#include "a.h"

int main() {
    a_t aigh;

    return 0;
}

Voila! This is it! No extra includes no nothing. We got em bois!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文