Pointers to Incomplete Types in Prototypes

Steve Summit scs at adam.mit.edu
Wed May 8 14:33:53 AEST 1991


In article <1991May7.043654.4795 at tkou02.enet.dec.com> diamond at jit533.enet@tkou02.enet.dec.com (Norman Diamond) writes:
>The standard might have benefitted from distinguishing between tag
>declarations and tag definitions, but it did not do so.

When writing my previous response on this topic, I found myself
wondering whether to use "declaring" or "defining" with respect
to struct types and tags.  Your bringing the issue out in the
open like this makes me realize that there is no meaningful
distinction.

When declaring objects and functions, we commonly (and
meaningfully) distinguish between declarations:

	extern int i;
	extern double square(double);

and definitions:

	int i = 314;
	double square(double x) { return x * x; }

Note that, in either case, an identifier and some type
information are entered into a symbol table.  A definition
additionally implies space allocation, and perhaps content
initialization.

However, a struct declaration, with or without a tag and with or
without a struct-declaration-list, only enters identifier and
type information into symbol tables.  We could make an arbitrary
distinction and use the term "struct definition" for all struct
declarations which include struct-declaration-lists, but this is
a very different distinction than that which is made between
object and function declarations and definitions, so it is
perhaps wise (and deliberate) that section 3.5.2.1 says that
"The presence of a struct-declaration-list in a struct-or-union-
specifier *declares* a new type, within a translation unit"
[emphasis mine].

>In article <kre.673334440 at mundamutti.cs.mu.OZ.AU> kre at cs.mu.oz.au (Robert Elz) writes:
>>It seems as if the compiler involved is treating the reference
>>to bar in...
>[
	type foo(struct bar *arg);
>]
>...as a definition, as it hadn't
>>seen a definition before.  That is surely a bug.
>
>Yes.  No wonder I was dissatisfied with my previous posting on this
>issue.  Something still seems wrong.

There's nothing wrong here.  In the absence of any previous
mention of a struct bar,

	type foo(struct bar *arg);

is as much of a struct "definition" as is

	type foo(struct bar { ... } *arg);

It can't be a "reference;" what (previous) struct bar could it be
referring to?

If mentioning

	struct bar *

didn't suffice to "define" a struct bar (assuming it wasn't
already "defined"), then the one-line translation unit

	struct bar *bp;

would have to give some kind of an error, much like the "warning:
struct bar never defined" which lint issues under the -h option.
(This is not usually a useful error message; I use grep -v to
eliminate it when I'm using -h with lint.)

The sole problem with

	extern foo(struct bar *arg);

at the top of some translation unit is that any call to foo later
in that translation unit can't pass a compatible pointer-to-
struct-bar.  (As has been noted, it could successfully pass a
null pointer.)  The problem is not so much that the (strictly
speaking) incompatible pointer-to-struct-bar that could be passed
would fail to work correctly over in the translation unit where
foo() is defined.  The problem is just that it is incompatible,
according to the rules.

If we had the two translation units

	main.c:					foo.c:

	extern foo(struct bar *);		struct bar {int i; };

	main()					foo(struct bar *bp)
	{					{
	struct bar {int i;} b;			bp->i = 3;
	foo(&b);				return 0;
	return 0;				}
	}

, then the struct bar with which x is defined in main.c *is*
compatible (by the special language in section 3.1.2.6) with the
struct bar which foo(), over in foo.c, accepts a pointer to.
x's struct bar is however *not* compatible with the struct bar
which the prototype at the top of main.c says foo() accepts a
pointer to.  The compiler is obliged to issue a diagnostic for
the call to foo() in main.c, whether we think it's meaningful or
not.  (I admit that it's dubious from an intuitive point of
view, but the compiler is just following the rules.)

Therefore, if a struct type is "defined" (either because of the
presence of a struct-declarator-list, or the first appearance of
a particular tag in the translation unit) inside of a prototype
declaration, it is immediately discernible as "odd," and a
lint-like warning is appropriate.  (I believe it was just such a
diagnostic which started this thread.  Whether compilers should
be issuing lint-like messages is of course another issue.)  I
thought there was a footnote or a sentence in the Rationale
specifically mentioning the uselessness of struct "definitions"
in prototypes, but I can't find it now.  (Finally, we might note
that the additional confusion evidently generated by this
particular lint-like message might be worse than the confusion it
was supposed to prevent, when the type passed to foo() is later
diagnosed as incompatible.)

                                            Steve Summit
                                            scs at adam.mit.edu



More information about the Comp.std.c mailing list