Suppose source file
A.c mostly deals with data structure
A, which is defined in
A.h. Likewise source file
B.c mostly deals with data structure
B, which is defined in
B.h.
A.h contains declarations of all the functions in
A.c, and
B.h contains declarations of all the functions in
B.c.
Now we need to add a function to
A.c which takes an argument of type
B. To add a declaration of this function to
A.h, we want a definition of type
B to be in place in
A.h. We do this by including
B.h in
A.h. This is intended to produce the effect that we can include
A.h in another source file to declare the functions implemented in
A.c, without requiring that source file to first include
B.h.
Now suppose we add a function to
B.c which takes an argument of type
A. We do exactly the same in reverse.
What happens when a source file includes
A.h now?
A.h includes
B.h at the start.
B.h includes
A.h at the start. We used include guards, so
A.h is not included. When we reach a declaration of a function in
B.h that has a parameter of type
A, no definition of type
A has been encountered yet, so there is an error.
This can be fixed by pulling the definitions of both
A and
B out into a separate header file, for example
types.h. Forward declarations of types might work as well. (However, as far as I remember forward declaring a
typedef isn't always accepted by C compilers.) In general, mixing function declarations and type definitions in header files may be problematical.