[...]
People have a few different ideas about how to handle this. I have my
own rules that I stick to very rigidly:
For any "module" you have a ".c" file and a ".h" file. Every function
(and file-scope variable) that the module defines is either local to the
module, or exported from the module. If it is local, it is declared as
"static", and then only at its definition. (Occasionally it is useful
with an extra "forward" declaration, but I don't use these unless they
are needed.) If it is to be exported, there is an "extern" declaration
in the module's header file. Modules /always/ #include their own
headers. There should normally never be an "extern" declaration in a
".c" file - keep it strictly in the appropriate header.
Thus here you will have:
a.h
===
#ifndef _a_h_
This choice of macro name is ill-advised, because "All
identifiers that begin with an underscore are always reserved
for use as identifiers with file scope [...]" and "Each [library]
header [...] optionally declares or defines identifiers [...]
which are always reserved either for any use or for use as file
scope identifiers" (both from 7.1.3p1). That is, the name may
already be in use by a standard library header, and using it for
any other purpose could cause trouble.
Some people form inclusion guards by capitalizing the name
of the header file (because macros are usually all-caps) and
appending _H, so the macro for "a.h" would be A_H. Unfortunately,
that practice breaks down when you get to "elevator.h" because
the description of <errno.h> (7.5p4) says "Additional macro
definitions, beginning with E and a digit or E and an uppercase
letter may also be specified by the implementation," so
ELEVATOR_H could be an implementation-defined error macro.
My own practice is to stick the H at the beginning, so the
inclusion guards would be H_A and H_ELEVATOR; these identifiers
are firmly in the programmer's name space. One place I worked
went further, encoding the header file's entire path:
/* graphics/utils/canvas.h */
#ifndef H_GRAPHICS_UTILS_CANVAS
/* nautical/tallship/supplies/canvas.h */
#ifndef H_NAUTICAL_TALLSHIP_SUPPLIES_CANVAS
.... so the two "canvas.h" headers could be used together.
A particularly important check you have here is that the "extern"
declaration is guaranteed to match the definition of the function,
because the same declaration is #include'd while compiling the body of
the function - any mismatches will cause an error.
The same place that used full-path inclusion guards also had
a rule that the .h should be the *first* thing included in the
corresponding .c, so the compiler would detect any failure on
the .h's part to include its own dependencies. That is,
/* path/a.h */
#ifndef H_PATH_A
#define H_PATH_A
extern void oldMill(FILE *stream);
#endif
/* path/a.c, right at the top: */
#include "a.h" // BZZZT! <stdio.h> not included yet
(Some people disapprove of nested inclusions; others feel that
telling a header's user to include four other headers first is
an imposition. I'm in the latter camp.)