Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!usc!snorkelwacker.mit.edu!bloom-picayune.mit.edu!news From: scs@adam.mit.edu (Steve Summit) Newsgroups: comp.std.c Subject: Re: Pointers to Incomplete Types in Prototypes Message-ID: <1991May8.043353.28983@athena.mit.edu> Date: 8 May 91 04:33:53 GMT References: <683g+p#@rpi.edu> <1991May7.043654.4795@tkou02.enet.dec.com> Sender: news@athena.mit.edu (News system) Reply-To: scs@adam.mit.edu Organization: Thermal Technologies, Cambridge, MA Lines: 129 In article <1991May7.043654.4795@tkou02.enet.dec.com> diamond@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@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@adam.mit.edu