A question on C programming style

Steve Summit scs at adam.mit.edu
Sat Apr 13 11:39:11 AEST 1991


In article <1991Apr12.103621.8907 at umiami.ir.miami.edu> devebw9f at miavax.ir.miami.edu writes:
>Style 1: (No nested includes - user responsible for proper order of includes).
>foo.h
>  extern save_data (FILE *fp);
>
>Style 2: (Nested inclusion).
>foo.h
>  #include <stdio.h>     /* We know that this has to be included with this. */
>  extern save_data (FILE *fp);

[A fine FAQ list question, this.]

Whether nested #included files are good style is, historically,
(like so many of these style questions :-( ) a bit of a religious
question.  (ANSI's new guarantees may have shifted the balance
somewhat.)

When one header file makes use of something (usually a macro or
typedef) defined in another, I feel that a nested #include
directive is a good idea.  The alternative -- requiring the
#includer to #include other header files first -- loses big
points in the information-hiding department, and leads to real
maintenance headaches in practice.  (It's too easy to forget to
pre-#include something else, and the errors that result are
typically non-obvious.  If the requirements of the header file in
question change, all its #includers must be modified.)

Obviously, if header files are to be #included recursively, they
must be "idempotent" (a word which only comes up in this
discussion, meaning something like "can be multiply invoked
safely"), since it is likely that they will end up getting
#included twice.  (In Bimal's second example, the #includer of
foo.h might well #include <stdio.h> anyway.)  The standard
technique for guaranteeing idempotency, which has already been
mentioned, is to "turn off" each include file if it has been
processed already:

foo.h:
	#ifndef FOO_H
	#define FOO_H

	/* contents of foo.h */

	#endif

To cut down on compilation time a bit (by eliminating unnecessary
file opening and namei overhead) some people prefer to do the
protecting in the #includer:

	#ifndef FOO_H_INCLUDED
	#define FOO_H_INCLUDED
	#include "foo.h"
	#endif

This is ugly, as the #ifndef has to be repeated (and the sentinel
macro name agreed upon) in each #includer.

Nested #include files can, in general, be confusing.  Finding out
where something is defined can require traversing a twisty little
maze of #include directives.  (This Gordian knot can be cut
easily, however, with "grep <pattern> *.h" .)  Manual Makefile
maintenance quickly becomes nearly impossible, so an automatic
Makefile generating scheme, which follows the nested #include
directives reliably, is a requirement.

The confusion and Makefile maintenance drawbacks lead some people
to recommend strongly against nested #include files.  (Some
people also feel that the #ifndef/#define technique is an
unacceptably ugly kludge.  As I said, it has been a religious
argument.)  However, I feel that the disadvantages of not using
nested #include files (the loss of information hiding, and the
burdensome requirements placed on each #includer) outweigh the
disadvantages of using them.  (As I've suggested, given the
existence of grep and a good Makefile dependency generator, the
disadvantages of nested #include files largely disappear.  And
the #ifndef/#define technique isn't really ugly, either,
especially if I remember to call it a "technique" and not a
"trick" :-) . )

I've slanted this discussion a bit towards the case when the file
being recursively #included is a "project" file, under the
programmer's control (and probably #included with "" rather than <>).
When the header file to be recursively #included is a standard
header file, as it was in Bimal's example, the options shift
slightly.

The ANSI C Standard guarantees that standard header files may
safely be #included multiple times, so nested #inclusion (of
standard headers) is safe in an ANSI environment.  However, most
pre-ANSI header files do not implement any idempotency, so
programmers must be careful if portability to pre-ANSI systems is
important.

Since the "clean" #ifndef/#define technique is implemented inside
the header file being protected, you can't apply it retroactively
to an older standard header file which lacks it.  (Even if you
have write access to the standard header files on your system,
and don't mind modifying them, you can't assume that a person you
give your code to can.)  However, for <stdio.h> at least, a
variation on the second, deprecated idempotency technique is
viable:

	#ifndef EOF
	#include <stdio.h>
	#endif

It's not at all unusual for a data structure or subroutine
interface defined in a header file to refer to the stdio FILE *
type (exactly as in Bimal's example), so usages like this are
common, and shouldn't be frowned upon.

(It helps if, in top-level .c files, you always #include standard
headers first, followed by "local" ones.  If, on the other hand,
you had things equivalent to

	#include "foo.h"
	#include <stdio.h>

, errors on pre-ANSI systems would be more likely.  It's easy to
protect "#include <stdio.h>" with "#ifndef EOF" inside of foo.h;
it's harder and messier to do so in every .c file.  "It is agreed
that the ice is thin here.")

[Now all I have to do is shrink this discussion down to three
sentences for the FAQ list.]

                                            Steve Summit
                                            scs at adam.mit.edu




More information about the Comp.lang.c mailing list