简体   繁体   中英

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

I have a very large program which is not compiling and I suspect it has to do with circular dependencies across structs. When I code it like the below, it will not compile

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

But if I completely muck up my design and create a common.h file, and put all the struct declarations in there, it seems to work.

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

This seems like really bad design. Furthermore, I though that the header guards were meant to prevent issues arising out of circular includes. Am I supposed to use the common.h approach, or am I doing something else wrong that it is making my first solution fail?

There is no problem with mutually recursive structures in different files.

The main problem here is that the circular inclusions don't work. It's easy to solve this, however, because neither header must include the other at all.

If you skip the typedef , you can do this:

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

and

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

Alternatively

If you want to use the typedef, you'll just have to skip using it inside the structure definitions.

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

and

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

With the following C file:

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

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

The preprocessor outputs the following (not including the contents of 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;
}

Note that the typedef for Foo comes after it is referenced. This is what causes the errors. Because Foo and Bar depend on each other, it is good practice to define both in the same header with the typedefs for each first.

I see an obvious problem-- On the header guard for foo.h , you're using the name of the struct as the guard. That should instead be all-caps for a macro. I would guess this is some sort of IDE auto-replace that slipped into your code by accident?

The problems you're having may extend beyond, this, but still. =]

Every type has to be declared (but not necessarily defined ) before you use it. This doesn't happen in your first approach. Suppose you were to include bar.h in your main program. Because foo.h is included there, and due to the structure of your header guards, the compiler generates something like:

typedef struct _foo Foo;

struct _foo{
  Bar *bar;
}

typedef struct _bar Bar;

struct _bar{
  Foo *foo;
}

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

Which it then tries to compile from top to bottom. The problem is that it hits the definition

struct _foo{
  Bar *bar;
}

and doesn't know what to do because the compiler hasn't seen Bar yet.

The solution is to use a forward declaration to declare to the compiler that that there will be a type called struct _bar and you promise that you'll define this type later. For example, the following compiles just fine:

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

In your case in particular it would be good form to either put all forward declarations in a dedicated header file, or to just forward declare each type in each header file. For example:

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

As has been pointed out in the comments, the header guards do not "solve" this problem for you- they merely prevent each header file from being included more than once (which typically causes redefinition errors). The correct solution is to make sure that the compiler knows about every type you're going to use before you use it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM