In article <11**********************@g14g2000cwa.googlegroups .com>
G Patel <ga********@gmail.com> wrote:
Pg. 140 of K&R2 shows an example of mutually referential structure
declarations...
[Vertically compressed]
struct t { struct s *p; };
struct s { struct t *q; };
[snippage]
How is this valid when the type 'struct s' is not in scope when *p is
declared as a member of the type struct t?
Structures -- which are C's general-purpose "create me a new type"
thingy -- can be declared, as well as defined.
The minimal syntax of such a declaration is:
struct tag;
while the minimal syntax of a definition that uses a tag[%] is:
struct tag { FIRST_MEMBER; };
In other words, without the open brace, you are merely mentioning
the *name* of a type, rather than defining that type. (The contents
inside the braces define the type itself.)
[%footnote: New structure types can be left un-named by omitting
the tag. This has relatively limited usefulness since the name
cannot then be repeated. On the other hand, it is possible to
"capture" the un-named type name using C's "typedef" keyword, which
-- despite its name -- does not actually define new types. Instead,
it defines aliases for existing types. If you capture the no-name
type under some alternative alias, you can use the alias. It is
a bit like referring to either Clark Kent as Superman: if Clark
Kent had not had a real name, you would have had to call him Superman
even when he was in disguise. :-) ]
Given a type-name, you can of course declare variables of that
type, and pointers to that type, and so on:
int i; /* this means "i" is an "int" */
char *cp; /* this means *cp is a "char" */
The same holds true for your own types:
struct type1 s1; /* i.e., s1 is a "type1" */
struct type2 *s2; /* i.e., *s2 is a "type2" */
If you have already defined your type, this is fine; if you have
not yet defined your type, it is what C calls an "incomplete type".
You can sometimes, but not always, declare ordinary variables that
have incomplete types; you can always declare pointers to incomplete
types. (More specifically -- and more confusingly -- if T is an
incomplete type, "T *", pointer-to-T, is itself a complete type.
It merely points to an incomplete type.)
If this is 'valid' please tell me why scopes don't apply here.
Scopes can in fact apply here:
struct s { long l; };
void f(void) {
struct s; /* this "vacuous" declaration clears the way */
struct s { char c; } var;
/* this is a new, different type also named s */
...
}
Note, however, that the scope is introduced by the definition of
function f(), not the braces around the structure members. (This
is one area where C and C++ differ: in C++, structure members can
have their own scope, limited to the structure. The C++ rules are
much more complicated than the C rules.)
I realize this is also confusing. I recommend never doing it, in
the same way that I recommend against code like:
int number_of_zorgs;
void f(void) {
int number_of_zorgs;
...
}
Here we have two ordinary "int" variables with the same name. It
becomes confusing if we attempt to talk about both variables at
the same time, since they both have the same name. (Since my own
given name is quite common, I have a lot of personal experience
with this problem. Sometimes I am in a group that has two or three
"Chris"es in it. Sometimes people resort to numbering us.)
With all that out of the way, let us revisit the mutually-referential
structure issue. Given:
struct s; /* declare type s without defining it */
struct t; /* and likewise for type t */
struct s { struct t *tp; }; /* now define s */
struct t { struct s *sp; }; /* and t */
it is easy enough to see that an "s" refers indirectly (via pointer)
to a "t", and a "t" refers indirectly to an "s". We declared both
ahead of time, and then defined both.
C, however, allows us to be sloppy. A type-name simply springs
into existence as soon as we mention it (using C's type-name-creating
keyword "struct", of course). So we do not have to declare the
two types in advance -- we can just start defining one, and declare
the other in the middle of the first one's definition. This is
what you had in your sample code.
There are some problems with this. Typos sneak in, as pointed out
in another thread earlier. If we declare or define a type called
"gargleblaster", it is easy to mis-type the name as "garbleblaster"
(garG => garB) at some point:
struct gargleblaster; /* optionally, insert { contents } here too */
...
extern void somefunc(struct garbleblaster *);
We never declared (much less defined) the misspelled name, so it
simply springs into existence as a new incomplete type. (Some use
this as an argument for using typedefs; and it is a valid argument
for that, despite my own tastes running the other way.)
Structure types *do* obey scopes (despite having none of their
own). In general, incomplete structure types are only usefully
completed in the same scope (it is possible to complete them in an
inner scope, but this has relatively little practical application).
Unfortunately for this particular case, function prototype arguments
have "prototype scope", which ends -- and can never be resurrected
-- at the close parenthesis ending the prototype sequence. Thus
the declaration of function somefunc() declares an incomplete type
("garbleblaster", our misspelled "gargleblaster") that can never
be completed. Better compilers generally produce a warning here.
The warning is, however, confusing until you understand this whole
paragraph.
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it
http://web.torek.net/torek/index.html
Reading email is like searching for food in the garbage, thanks to spammers.