[on forward declarations of variables via tentative definitions]
I suppose the reason I never remember this sort of two-step process is
because I'm never had a situation where it was useful. I don't find
the OP's header file example convincing.
Forward declarations of variables are required for the same
reason forward declarations of functions are required: sometimes
it is impossible to topological-sort-away the dependencies.
Suppose we have function f() that calls function g(), and function
g() that calls function f(). This order:
/* optional: static */
F_TYPE f(args) {
...
g(...);
...
}
/* optional: static */
G_TYPE g(args) {
...
f();
...
}
calls f() from g() with a prototype in place, but calls g() from
f() without one. If we swap the order, putting g()'s definition
first, then f() calls g() with a prototype, but g() calls f()
without one. The solution is to declare at least one of f() and
g() first, then define the two in the remaining topologically-required
order (if we declare both, no order is required).
Just as the function version of the problem shows up in mutually-recursive
functions, the data version shows up in mutually-referential data.
Suppose we have the following data structures:
/* optional: typedef struct S STYPE; */
typedef struct T TTYPE; */
struct S {
struct T *s_tref; /* or TTYPE *s_tref; */
...
};
struct T {
struct S *t_sref; /* etc */
...
};
but now we want to define a static-duration, and optionally
internal-linkage, instance of an S that points to a T, and vice
versa:
/* optional: static */
struct S s_instance = { &t_instance, ... };
/* optional: static */
struct T t_instance = { &s_instance, ... };
As with functions f() and g(), neither order works without a
forward declaration. If s_instance and t_instance are to have
external linkage, we can use the "extern" keyword to "forward
declare" at least one of them:
extern struct T t_instance; /* forward declaration */
struct S s_instance = { &t_instance, ... };
struct T t_instance = { &s_instance, ... };
But if you wish to keep s_instance and t_instance private, with
internal linkage (using the "static" keyword), "extern" is no
longer suitable; now you must use "static":
static struct T t_instance; /* forward decl & tentative def */
static struct S s_instance = { &t_instance, ... };
static struct T t_instance = { &s_instance, ... };
As with functions f() and g(), we can forward-declare both instances
if we like, but at least one -- and in this case at most one (more
complicated multi-way references may require up to N-1 forward
declarations of N objects) -- is required.