How can I stop #including redundant headers when I use header files like these?
c, compiler-construction
Solution
What do you mean by 'include'? The preprocessor statement `#include file` copies the contents of `file` and replaces the statement with these contents. This happens no matter
If by 'include' you mean "the statements and symbols in these files will be parsed multiple times causing warnings and errors", then no, the include guards will prevent that.
If by 'include' you mean "some part of compiler will read some part of these files", then yes, they'll be included multiple times. The preprocessor will read the second inclusion of the file and replace it with a blank line because of the include guards, which incurs a tiny overhead (the file is already in memory). Modern compilers (GCC, not sure about others) will probably be optimized to avoid this, however, and note that the file has include guards on the first pass and simply discard future inclusions, removing the overhead - Don't worry about speed here, clarity and modularity are more important. Compilation is a time-consuming process, for sure, but `#include` is the least of your worries.
To better understand include guards, consider the following code sample:
#ifndef INCLUDE_GUARD
#define INCLUDE_GUARD
// Define to 1 in first block
#define GUARDED 1
#endif
#ifndef INCLUDE_GUARD
#define INCLUDE_GUARD
// Redefine to 2 in second block
#define GUARDED 2
#endif
After (the first pass of) preprocessing, what will `GUARDED` be defined to? The preprocessor statement `#ifndef` or its equivalent, `#if !defined()` will return `false` if their argument is indeed defined. Therefore, we can conclude that the second `#ifndef` will return false, so only the first definition of GUARDED will remain after the first pass of the preprocessor. Any instance of `GUARDED` remaining in the program will be replaced by 1 on the next pass.
In your example, you've got something slightly (but not much) more complicated. Expanding all the `#include` statements in ExampleClient.c will result in the following source: (Note: I indented it, but that's not normal style for headers and the preprocessor won't do it. I just wanted to make it more readable)
/* ExampleClient.c */
//#include <stdlib.h>
#ifndef STDLIB_H
#define STDLIB_H
int abs (int number); //etc.
#endif
//#include <stdio.h>
#ifndef STDLIB_H
#define STDLIB_H
#define NULL 0 //etc.
#endif
//#include "mpi.h"
#ifndef MPI_H
#define MPI_H
void MPI_Func(void);
#endif
//#include "foo.h"
#ifndef FOO_H
#define FOO_H
//#include <stdlib.h>
#ifndef STDLIB_H
#define STDLIB_H
int abs (int number); //etc.
#endif
//#include "mpi.h"
#ifndef MPI_H
#define MPI_H
void MPI_Func(void);
#endif
void foo(void);
#endif
//#include "bar.h"
#ifndef BAR_H
#define BAR_H
//#include <stdlib.h>
#ifndef STDLIB_H
#define STDLIB_H
int abs (int number); //etc.
#endif
//#include "mpi.h"
#ifndef MPI_H
#define MPI_H
void MPI_Func(void);
#endif
void bar(void);
#endif
void main(int argc, char *argv[]) {
foo();
MPI_Func();
bar();
exit(0); // Added missing semicolon
}
Go through that code and note when various definitions are performed. The result is:
#define STDLIB_H
int abs (int number); //etc.
#define STDLIB_H
#define NULL 0 //etc.
#define MPI_H
void MPI_Func(void);
#define FOO_H
void foo(void);
#define BAR_H
void bar(void);
With respect to your request for other criticism/pointers, why are you #including stdlib.h and mpi.h in all your headers? I understand that this is a stripped down example, but in general, header files should only include files necessary for the declaration of their contents. If you use a function from stdlib or call MPI_func() in foo.c or bar.c, but the function declarations are simply `void foo(void)`, you shouldn't include these files in the header function. For example, consider the following module:
foo.h:
#ifndef FOO_H
#define FOO_H
void foo(void);
#endif
foo.c:
#include <stdlib.h> // Defines type size_t
#include "mpi.h" // Declares function MPI_func()
#include "foo.h" // Include self so type definitions and function declarations
// in foo.h are available to all functions in foo.c
void foo(void);
size_t length;
char msg[] = "Message";
MPI_func(msg, length);
}
In this example, the implementation of `foo()` requires stuff from stdlib and mpi, but the definition does not. If foo() returned or required a `size_t` value (typedef'ed in stdlib), you'd need to #include stdlib in the .h file.
Problem
So I'm still getting used to modular programming, and want to make sure I'm adhering to best practices. If I have the two module header files below, will the the headers `#included` by each file (for example "mpi.h") be included multiple times? Is there a proper way to account for this? Also, my module headers typically look like these examples, so any other criticism/pointers would be helpful. ``` /* foo.h */ #ifndef FOO_H #define FOO_H #include <stdlib.h> #include "mpi.h" void foo(); #endif ``` and ``` /* bar.h */ #ifndef BAR_H #define BAR_H #include <stdlib.h> #include "mpi.h" void bar(); #endif ``` And use the sample program: ``` /* ExampleClient.c */ #include <stdlib.h> #include <stdio.h> #include "mpi.h" #include "foo.h" #include "bar.h" void main(int argc, char *argv[]) { foo(); MPI_Func(); bar(); exit(0) } ```