[comp.lang.c] incomplete types

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/13/89)

In article <1567@ubu.warwick.UUCP> geoff@cs.warwick.ac.uk (Geoff Rimmer) writes:
>In article <9804@smoke.BRL.MIL> gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
>> In article <7488@june.cs.washington.edu> ka@june.cs.washington.edu (Kenneth Almquist) writes:
>> >I've been told that incomplete types cannot be used in function prototypes.
>> I couldn't find such a prohibition in the pANS.
>> (But maybe it's there and I missed it.)
>gcc will not allow it:
>static void function (struct egg * a);
>struct egg { int number1; float number2; };
>static void function (struct egg * a)
>{
>        printf("In function with [%d,%f]\n",a->number1,a->number2);
>}
>str.c:1: warning: `struct egg' declared inside parameter list
>str.c: In function function:
>str.c:6: conflicting types for `function'
>str.c:1: previous declaration of `function'
>The strange thing is that it doesn't barf on the 
>	struct void function (struct egg *a);
>line, even though the structure is as yet undefined.  The problem is
>when it compares the types of the function's arguments, and sees that
>in the call to the function, the argument is type (struct egg *) and
>in the declaration it is unknown.
>Is this a problem with gcc, or does this happen with all ANSI compilers?

Sometimes I wonder if folks in the UK speak the same language.

The first warning from GCC is correct: that structure tag "egg" has
function prototype scope, and although (I believe) an incomplete type
may be used in the declaration, the type must eventually be completed.
Once the end of the function declaration is reached it is obviously
impossible for that "egg"-tagged structure's type to ever be completed,
since the scope of the tag is gone at that point.

The "conflicting types" and "previous declaration" warnings are not
incorrect: the full type of the first declaration of "function" is
unknown due to the earlier problem, whereas the second declaration
(part of the function definition) has a definite type.  One supposes
that that is a mismatch of declaration types.

I don't understand what you mean by "doesn't barf ... even though the
structure is as yet undefined".  That's essentially what the first
warning was about.  If the whole mess had been preceded by
	struct egg;
then there would have been a structure tag "egg" in scope that would
have been taken as the one for the function parameter.  It would
still be an incomplete type, but one that is in principle possible to
complete later in the translation unit (as indeed occurs at the next
statement).  So far as I have been able to determine, that should be
perfectly valid usage.

There is nothing inherently "bad" about incomplete types, especially
incomplete structure declarations.  In fact they are essential for
declaring structures that contain pointers to each other.

leo@philmds.UUCP (Leo de Wit) (03/14/89)

In article <9842@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
|There is nothing inherently "bad" about incomplete types, especially
|incomplete structure declarations.  In fact they are essential for
|declaring structures that contain pointers to each other.

How essential that is? Consider:

struct egg {
     struct hen {
          struct egg *eggp;
     } *henp;
};

This declares two structure types, each containing a pointer to the
other one. And I can give more complex examples (a nice one is the
grammar for the Bourne shell, which can be expressed in these type of
recursive data structures: essentially one big struct definition).

    Leo.

mnc@m10ux.UUCP (Michael Condict) (03/15/89)

In article <976@philmds.UUCP>, leo@philmds.UUCP (Leo de Wit) writes:
- In article <9842@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
- |There is nothing inherently "bad" about incomplete types, especially
- |incomplete structure declarations.  In fact they are essential for
- |declaring structures that contain pointers to each other.
- 
- How essential that is? Consider:
- 
- struct egg {
-      struct hen {
-           struct egg *eggp;
-      } *henp;
- };
- 
- This declares two structure types, each containing a pointer to the
- other one. And I can give more complex examples ...

Yes, but in the example you show, the scope of the "struct hen" tag declaration
might be limited to the inside of the "struct egg" declaration, depending
on your C compiler and the rules of ANSI-C.  Can you show us how to
declare these two structs, hen and egg, such that both are guaranteed by the
rules of ANSI-C and current C compilers to be accessible after the declaration,
without using incomplete types?

Even if the above example declares the two tags egg and hen globally, it is
awkward and confusing to have to arbitrarily declare one inside the other.
It will only lead to very confusing error messages when you try to add another
mutually recursive struct declaration and don't get the ordering or the nesting
right.  Things like struct tags that are global, should be declared only
globally, i.e. outside of any function or other struct.

By the way, can someone tell me if the above code defines "struct hen" globally
in ANSI-C?
-- 
Michael Condict		{att|allegra}!m10ux!mnc
AT&T Bell Labs		(201)582-5911    MH 3B-416
Murray Hill, NJ

throopw@agarn.dg.com (Wayne A. Throop) (03/16/89)

> leo@philmds.UUCP (Leo de Wit)
>| gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>)
>|There is nothing inherently "bad" about incomplete types, especially
>|incomplete structure declarations.  In fact they are essential for
>|declaring structures that contain pointers to each other.
> How essential that is? Consider:
>     struct egg {
>          struct hen {
>               struct egg *eggp;
>          } *henp;
>     };

Ok.  I've considered it.  The type "struct egg" is incomplete
at the point it is used, namely in the line

                struct egg *eggp;

So I'd say Leo hasn't come up with an example where mutually
referential structures can be defined without resorting to
incomplete types.

As far as I can see, it's still essential.

--
All the system's paths must be topologically and circularly
interrelated for conceptually definitive, locally transformable,
polyhedronal understanding to be attained in our spontaneous -- ergo,
most economical -- geodesiccally structured thoughts.
                              --- R. Buckminster Fuller
--
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

leo@philmds.UUCP (Leo de Wit) (03/18/89)

In article <884@m10ux.UUCP> mnc@m10ux.UUCP (Michael Condict) writes:
|In article <976@philmds.UUCP>, leo@philmds.UUCP (Leo de Wit) writes:

  [] self-contained cross-referencing struct example omitted ...

|Yes, but in the example you show, the scope of the "struct hen" tag declaration
|might be limited to the inside of the "struct egg" declaration, depending
|on your C compiler and the rules of ANSI-C. 

There's no such thing as declaration scoped types (if there is I would
be very interested to know where you got that information); the scope
of struct hen is the same as the scope of struct egg (the same block of
whatever).

|                                             Can you show us how to
|declare these two structs, hen and egg, such that both are guaranteed by the
|rules of ANSI-C and current C compilers to be accessible after the declaration,
|without using incomplete types?

Yes, exactly as it stands. I would be very surprised if (draft) ANSI
conformant compilers couldn't handle this. And the C compilers I've
seen do treat it correctly.

|Even if the above example declares the two tags egg and hen globally, it is
|awkward and confusing to have to arbitrarily declare one inside the other.

Ah, that's the real problem. Perhaps you're having trouble reading this
declaration.  As a matter of fact, I have used such declarations more
than once (although one doesn't need cross-referencing structures every
day), and I never found it awkward nor confusing. The declaration not
being symmetrical is more a style issue.

|It will only lead to very confusing error messages when you try to add another
|mutually recursive struct declaration and don't get the ordering or the nesting
|right.

Something like this?

struct egg {
     struct hen {
          struct chicken {
              struct egg *eggp;
          } *chickenp;
     } *henp;
};

|        Things like struct tags that are global, should be declared only
|globally, i.e. outside of any function or other struct.

Struct tags? The struct _type_ (nl. struct hen) is what we are
discussing (the tag 'henp' is also available being a member of the
struct egg, but that's not the point), and as I pointed out before, has
the same scope as the struct egg.

    Leo.

leo@philmds.UUCP (Leo de Wit) (03/18/89)

In article <979@philmds.UUCP> leo@philmds.UUCP (me) writes:
|Struct tags? The struct _type_ (nl. struct hen) is what we are ....

Forget what I said here. My vocabulary was way off.

    Leo.