在两个不同的 header 文件中的两个结构中包含循环依赖是错误的吗?

Is it wrong to include circular dependencies in two structs in two different header files?

我有一个非常大的程序没有编译,我怀疑它与跨结构的循环依赖有关。当我像下面这样编码时,它不会编译

foo.h

#ifndef FOO
#define FOO

#include "bar.h"


typedef struct _foo Foo;

struct _foo{
  Bar *bar;
}

#endif

bar.h

#ifndef BAR
#define BAR

#include "foo.h"

typedef struct _bar Bar;

struct _bar{
  Foo *foo;
}

#endif

但是如果我完全搞砸了我的设计并创建一个 common.h 文件,并将所有结构声明放在那里,它似乎可以工作。

common.h

#ifndef COMMON
#define COMMON

typedef struct _foo Foo;
typedef struct _bar Bar;

#endif

foo.h

#ifndef FOO
#define FOO

#include "common.h"
#include "bar.h"

struct _foo{
  Bar *bar;
}

#endif

bar.h

#ifndef BAR
#define BAR

#include "common.h"
#include "foo.h"

struct _bar{
  Foo *foo;
}

#endif

这看起来真是糟糕的设计。此外,我认为 header 守卫是为了防止循环包含引起的问题。我应该使用 common.h 方法,还是我做错了什么导致我的第一个解决方案失败?

我看到一个明显的问题-- 在 foo.h 的 header 守卫中,您使用结构的名称作为守卫。对于宏,应该改为 all-caps。我猜这是某种 IDE auto-replace 不小心溜进了你的代码?

您遇到的问题可能不止于此,但仍然如此。 =]

不同文件的相互递归结构没有问题

这里的主要问题是循环包含不起作用。然而,解决这个问题很容易,因为 header 根本不需要包含另一个。

如果您跳过 typedef,您可以这样做:

// In a.h
struct a {
    struct b *ptr;
};

// In b.h
struct b {
    struct a *ptr;
};

或者

如果您想使用 typedef,您只需跳过在结构定义中使用它。

// In a.h
struct a {
    // Have to use 'struct b' instead of 'b' because we can't guarantee
    // that the typedef for b is visible yet.
    struct b *ptr;
};
typedef struct a a;

// In b.h
struct b {
    // Have to use 'struct a' instead of 'a' because we can't guarantee
    // that the typedef for a is visible yet.
    struct a *ptr;
};
typedef struct b b;

使用以下 C 文件:

#include <stdio.h>
#include "foo.h"
#include "bar.h"

int main()
{
    Foo f = { NULL };
    Bar b = { NULL };
    printf("hello\n");
    return 0;
}

预处理器输出如下(不包括stdio.h的内容):

# 2 "x1.c" 2
# 1 "foo.h" 1



# 1 "bar.h" 1



# 1 "foo.h" 1
# 5 "bar.h" 2

typedef struct _bar Bar;

struct _bar{
  Foo *foo;
}
# 5 "foo.h" 2
typedef struct _foo Foo;

struct _foo{
  Bar *bar;
}
# 3 "x1.c" 2


int main()
{
    Foo f = { ((void *)0) };
    Bar b = { ((void *)0) };
    printf("hello\n");
    return 0;
}

请注意,Foo 的类型定义 被引用之后出现。这就是导致错误的原因。因为 FooBar 相互依赖,所以最好在同一个标​​头中定义两者,并首先为每个定义类型。

每种类型在使用前都必须声明(但不一定定义)。这不会发生在您的第一种方法中。假设您要在主程序中包含 bar.h。因为 foo.h 包含在那里,并且由于 header 守卫的结构,编译器生成如下内容:

typedef struct _foo Foo;

struct _foo{
  Bar *bar;
}

typedef struct _bar Bar;

struct _bar{
  Foo *foo;
}

int main( int argc, char* argv[] ){
  //Program stuff
}

然后它会尝试从上到下编译。问题是它符合定义

struct _foo{
  Bar *bar;
}

并且不知道该怎么做,因为编译器还没有看到 Bar

解决方案是使用 前向声明 向编译器声明将有一个名为 struct _bar 的类型,并且您保证您将定义它稍后输入。例如,下面的编译就好了:

struct _bar; //Forward declaration of 'struct _bar'

struct _foo{ //Definition of 'struct _foo'
  struct _bar myBar;
}

struct _bar{ //Definition of 'struct _bar'
  struct _foo myFoo;
}

特别是在您的情况下,最好将所有前向声明放在专用的 header 文件中,或者只在每个 header 文件中前向声明每种类型。例如:

bar.h

#ifndef BAR
#define BAR

struct _foo; //Defined in foo.h, circular dependency

struct _bar{
  struct _foo *foo;
}

#endif

foo.h

#ifndef FOO
#define FOO

struct _bar; //Defined in bar.h, circular dependency

struct _foo{
  struct _bar *bar;
}

#endif

正如评论中指出的那样,header 守卫不会为您 "solve" 这个问题 - 他们只是防止每个 header 文件被多次包含(这通常会导致重新定义错误)。正确的解决方案是确保编译器在使用之前了解您将要使用的每种类型。