ggodw000 wrote:
As far as I remember including file1.h in file2.h is not an exactly proper practice, instead I included in file2.c which contains function declaration:
void fcn1(sName * p1) {...} but it does not seem to be compile unless explicitly include file1.h in file2.h.
You need "elaborated type specifier". I am not sure that the C standard even uses such terminology, because you always have to elaborate the types anyway. That is, the type name must use the "struct" prefix explicitly, as in "void fcn1(struct sName * p1)". Unless you use a typedef, in which case you can refer to the type without qualification (but it is a different name living in a different identifier namespace - those of typedefs).
Regarding the header inclusion, the practices vary and the general situation is not resolved. Your mileage will vary from project to project and from source to source. Including many headers can gradually accumulate to point where you have longer build times. In C this is less of a problem, but can get pronounced if your headers have a lot of inline functions. The issue is more compounded for C++ with templates, because their syntax is hard to parse and they can be instantiated multiple times in declarations. Anyway, for inline entities the compiler does at least some preliminary parsing of the code and error checking. Another issue that may occur is that you may get cyclic header dependencies. This happens in larger projects.
Note that for function prototypes, you don't need type definitions, but just forward declarations. Those are automatic for structures at the point of their use in the prototypes, so you don't need to include any header at all, although you may want to declare (not define) them globally to avoid gcc warnings. For typedefs, you need the definition in every header in form equivalent to their original definition, but you don't need to have the definition of the type they alias. Some examples with appropriate comments:
file1.h:
Code:
typedef struct sName { int x } tName;
file2.h:
Code:
struct sName; // forward declaration
void fcn1(struct sName *p1); // no need for the sName definition; the declaration above is sufficient
typedef struct sName tName; // typedef must be redefined, but does not need the full sName definition
void fcn1(tName *p1); // typedefs do not need elaboration
struct sNameB { struct sName *ptr; }; // Pointer member does not require the full definition of sName
file3.h:
Code:
#include "file1.h"
struct sNameB { struct sName obj; }; // Sub-object member requires the full type definition, hence the include
file2.c:
Code:
#include "file2.h"
void fcn1(struct sName *p1) { ... } // The function does not need the full sName definition, unless it is going to use sizeof for malloc or directly access the members inside
void fcn1(tName *p1) { ... } // ditto
file4.c:
Code:
#include "file1.h"
void fcn1(struct sName param) { ... } // This function uses by-value parameter and need the full sName definition