1

So I have two structs, which for simplicity we shall call A and B. A contains a pointer to B, and B contains an A. So here is the code:

a.h

#ifndef A_H
#define A_H

#include "b.h"

typedef struct _A {
    B *b;
} A;

#endif

b.h

#ifndef B_H
#define B_H

#include "a.h"

typedef struct _B {
    A a;
} B;

#endif

Now the issue is that when I import a.h from my main c file, I get errors about how A is an unknown type from b.h. I'm not sure how to resolve this.

anastaciu
  • 23,467
  • 7
  • 28
  • 53
dangee1705
  • 3,445
  • 1
  • 21
  • 40
  • sorry i was missing the end of the typedef. B contains an A, A has a pointer to B – dangee1705 Feb 10 '20 at 15:29
  • It's better to avoid circular references. Of course if it's possible. – Renat Feb 10 '20 at 15:30
  • Fix the file names; you have “A.h” in one place and “a.h” in another. These are different in some case-sensitive file systems. – Eric Postpischil Feb 10 '20 at 15:32
  • 1
    `struct _A` and `struct _B` are reserved names. Names that start with two underscores or an underscore and a capital letter are reserved and should not be used. See https://stackoverflow.com/questions/25090635/use-and-in-c-programs – Andrew Henle Feb 10 '20 at 15:51
  • Use `typedef struct B { … } B;` — the structure (and union and enumeration) tags are in a separate namespace from 'ordinary identifiers' such as typedef names so there is no collision. – Jonathan Leffler Feb 10 '20 at 16:10

4 Answers4

8

The way to do this is to pre-define the structs using empty definition

typedef struct _A A;
typedef struct _B B;

typedef struct _A {
    B *b;
} A;

typedef struct _B {
    A a;
} B;

You can put the pre-definitions in a global include file to include from wherever you need.

`

Sos Sosowski
  • 157
  • 3
2

Forward declare struct _B and remove #incude "b.h" in a.h.

Since a.h only contains a pointer to B it doesn't need to know it's data structure, so in A we only need to declare it's name.

Live sample

a.h

#ifndef A_H
#define A_H

struct _B; //forward declaration

typedef struct _A {
    struct _B* b;
} A;

#endif

b.h

#ifndef B_H
#define B_H

#include "a.h"

typedef struct _B {
    A a;
} B;

#endif
anastaciu
  • 23,467
  • 7
  • 28
  • 53
2

You need to understand the difference between a declaration and definition. For the moment, I put typedef aside. In order to be able to do this definition:

struct A {
    struct B *b;
};

you must FIRST declare struct B. Note that if you have declared something before defining it, the definition counts both as a definition and a declaration. But in this case, because of the circular dependency, we need separate declarations. You can solve that with:

struct A;
struct B;

Those two lines basically says "it exists two structs, and their names are A and B".

In most cases, the preferred solution would be something like this:

a.h

#ifndef A_H
#define A_H
typedef struct A A;
#endif

Similar for b.h

a.c

#include "a.h"
#include "b.h"

struct A {
    struct B* B;
};

Note, do not use different names for the struct and the typedef unless you can come up with a good reason not to. (I bet you cannot) and if you decide to do that anyway, it's not advisable to start with an underscore, because those are reserved for future use.

I wrote a related answer on this topic: https://stackoverflow.com/a/54752982/6699433

klutt
  • 30,332
  • 17
  • 55
  • 95
  • This answer opens with “You need to understand the difference between a declaration and definition” but does not provide information or analysis that would provide that understanding. In fact, in the terminology of the C standard, types are never defined. Identifiers of objects, functions, enumeration constants, and typedef names are defined. Types, including structures and structure tags are declared, incompletely or completely, but I do not see in the standard that they are referred to as defined. – Eric Postpischil Feb 10 '20 at 16:41
1

Here is a solution in which neither header needs to know the structure tag used by the other header. (Notably, struct B in B.h can be changed to struct foo with no changes in A.h. And A.h does not need a tag at all; struct A can be deleted.) This solution also allows a source file to include either header or both headers in either order.

A.h can be:

#include "B.h"

#if !defined A_h
#define A_h

typedef struct A
{
    B *b;
} A;

#endif  //  #if !defined A_h

and B.h can be:

#if !defined B_h
#define B_h

typedef struct B B;

#include "A.h"

typedef struct B
{
    A a;
} B;

#endif  //  #if !defined B_h

B.h works by performing a partial (incomplete) definition of B, in which the structure is known only by its tag, not its contents. Then it includes A.h to get a complete information about A, which uses B by pointer only (which is allowed for incomplete structure types). B.h finishes by completely declaring B.

A.h works by ensuring B.h is included first. It does this before its header guard so that the inclusion of A.h inside B.h will completely include A.h, which B.h needs.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312