20.2 Nested Header Files

Suppose you have written a little set of functions which you expect that other parts of your program (or other parts of other people's programs) will call. And, so that it will be easier for you (and them) to call the functions correctly, suppose that you have written a header file containing external prototype declarations for the functions. And, suppose that the prototypes look like this:

	extern int f1(int);
	extern double f2(int, double);
	extern int f3(int, FILE *);
You might put these three declarations in a file called funcs.h.

For now, we don't need to worry about what these three functions might do, other than to notice that f3 obviously reads from or writes to a FILE * stdio stream.

Now, suppose that you have a source file containing a function which calls f1 and/or f2. At the top of that source file, you would put the line

	#include "funcs.h"
However, if you were unlucky, the compiler would get down to the line
	extern int f3(int, FILE *);
within funcs.h and complain, because it would not know what a FILE is and so would not know how to think about a function that accepts a pointer to one. If the calling program (that is, the source file that included "funcs.h") didn't call f3 or printf or fopen or any of the other stdio functions, it would have no reason to include <stdio.h>, and FILE would remain undefined. (If, on the other hand, the source file in question did happen to include <stdio.h>, and if it included it before it included "funcs.h", there would be no problem.)

What's the right thing to do here? We could say that anyone who included "funcs.h" always had to include <stdio.h>, first. But you can think of header files a little bit like you think of functions: it's nice if they're ``black boxes'', if you don't have to worry about what's inside them, if you don't have to worry about including them in a certain order.

Another way to think about the situation is this: since the prototype for f3 inside of funcs.h needs stdio.h, maybe we should put the line

	#include <stdio.h>
right there at the top of funcs.h! Is that legal? Can the preprocessor handle seeing an #include directive when it's already in the middle of processing another #include directive? The answer is that yes, it can; header files (that is, #include directives) may be nested. (They may be nested up to a depth of at least 8, although many compilers probably allow more.) Once funcs.h takes care of its own needs, by including <stdio.h> itself, the eventual top-level file (that is, the one you compile, the one that includes "funcs.h") won't get error messages about FILE being undefined, and won't have to worry about whether it includes <stdio.h> or not.

Or will it? What if the top-level source file does include <stdio.h>? Now <stdio.h> will end up being processed twice, once when the top-level source file asks for it, and once when funcs.h asks for it. Will everything work correctly if <stdio.h> is included twice? Again, the answer is yes; the Standard requires that the standard header files protect themselves against multiple inclusion.

It's good that the standard header files are protected in this way. But how do they protect themselves? Suppose that we'd like to protect our own header files (such as funcs.h) in the same sort of way. How would we do it?

Here's the usual trick. We rewrite funcs.h like this:

	#ifndef FUNCS_H
	#define FUNCS_H

	#include <stdio.h>

	extern int f1(int);
	extern double f2(int, double);
	extern int f3(int, FILE *);

	#endif
All we've done is added the #ifndef and #define lines at the top, and the #endif line at the bottom. (The macro name FUNCS_H doesn't really mean anything, it's just one we don't and won't use anywhere else, so we use the convention of having its name mimic the name of the header file we're protecting.) Now, here's what happens: the first time the compiler processes funcs.h, it comes across the line
	#ifndef FUNCS_H
and FUNCS_H is not defined, so it proceeds. The very next thing it does is to #define the macro FUNCS_H (with a replacement text of nothing, but that's okay, because we're never going to expand FUNCS_H, just test whether it's defined or not). Then it processes the rest of funcs.h, as usual. But, if that same run of the compiler ever comes across funcs.h for a second time, when it comes to the first #ifndef FUNCS_H line again, FUNCS_H will at that point be defined, so the preprocessor will skip down to the #endif line, which will skip the whole header file. Nothing in the file will be processed a second time.

(You might wonder what would tend to go wrong if a header file were processed multiple times. It's okay to issue multiple external declarations for the same function or global variable, as long as they're all consistent, so those wouldn't cause any problems. And the preprocessor also isn't supposed to complain if you #define a macro which is already defined, as long as it has the same value, that is, the same replacement text. But the compiler will complain if you try to define a structure type you've already defined, or a typedef you've already defined (see section 18.1.6), etc. So the protection against multiple inclusion is important in the general case.)

When header files are protected against multiple inclusion by the #ifndef trick, then header files can include other files to get the declarations and definitions they need, and no errors will arise because one file forgot to (or didn't know that it had to) include one header before another, and no multiple-definition errors will arise because of multiple inclusion. I recommend this technique.

In closing, though, I might mention that this technique is somewhat controversial. When header files include other header files, it can be hard to track down the chain of who includes what and who defines what, if for some reason you need to know. Therefore, some style guides disallow nested header files. (I don't know how these style guides recommend that you address the issue of having to require that certain files be included before others.)


Read sequentially: prev next up top

This page by Steve Summit // Copyright 1996-1999 // mail feedback