pcg@aber-cs.UUCP (Piercarlo Grandi) (01/12/90)
In article <494@ucl-cs.UUCP> T.Day@ucl-cs.UUCP writes: From: Tim Day <T.Day@uk.ac.ucl.cs> Piercarlo "Peter" Grandi writes: > using 'struct some *' before having seen 'struct some {...' > is legal and without problem in both C and C++ Not quite. You have to say struct frob; before you can use pointers or references to frob. Not really, neither in C nor in C++. Try compiling: struct a {struct b *b;} a; struct b {struct a *a;} b; It shall work, both in C and C++. There has never been a requirement that the name of a struct be known before its use, if its use is to declare a pointer to the struct. The compiler need not bother; anything that comes after 'struct' must be a struct name, and a pointer has always the same size. You are confusing this point with the the fact that in C++, saying 'struct a;', which in it as well as in C++ is an empty declaration, and no special construct all, also has the effect of typedef'ing 'a' as 'struct a'. The compiler needs to know that a name is a typedef, otherwise parsing declarations cannot be done. In this, the C++ practice of saying 'class c;' is NOT a forward declaration; it i just the old harmless practice of having empty declarations, coupled with the new side effect of having an implicit typedef. Saying beforehand 'class c;' in C++ is not needed if you write 'class c *m;' instead of 'c *m;'. The effect is the same if you just say 'typedef class a ;'. With the following preprocessor macro you get the same effect in C: #define mode(FLAVOR,NAME) \ typedef FLAVOR NAME NAME; FLAVOR NAME Note that the typedef appears *before* the struct definition, if any; if you follow this with a ';' the struct definition becomes a harmless empty delcaration. You can use this in C as mode(struct,complex) { float re,im; }; complex a,b,c; or mode(struct,clutch); mode(struct,car) { ...; clutch *theClutch; ... }; just like in C++, if you have got fed up with using leading 'struct', 'union', 'enum' (enums are special though in C)... -- Piercarlo "Peter" Grandi | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk Dept of CS, UCW Aberystwyth | UUCP: ...!mcvax!ukc!aber-cs!pcg Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk
mike@umn-cs.CS.UMN.EDU (Mike Haertel) (01/13/90)
In article <1581@aber-cs.UUCP> pcg@cs.aber.ac.uk (Piercarlo Grandi) writes: >Not really, neither in C nor in C++. Try compiling: > > struct a {struct b *b;} a; > struct b {struct a *a;} b; > >It shall work, both in C and C++. There has never been a >requirement that the name of a struct be known before its use, >if its use is to declare a pointer to the struct. Just to be pedantic, this is not strictly true. It is OK to declare a pointer to an incomplete type, so this works *here*. But consider the following counterexample: /*1*/ struct b { int x; }; /*2*/ /*3*/ void foo() { /*4*/ struct b; /*5*/ struct a { struct b *b; } *a; /*6*/ struct b { struct a *a; } *b; /*7*/ /*8*/ b = a->b; /*9*/ } In this case, line 4 is necessary to make "struct b" an incomplete type. Otherwise, the "struct b *b" in line 5 would refer to the "struct b" declared on line 1, and line 8 would be a pointer type mismatch. The moral of the story is to declare all your types at file scope. -- Mike Haertel <mike@ai.mit.edu> "Everything there is to know about playing the piano can be taught in half an hour, I'm convinced of it." -- Glenn Gould
rfg@ics.uci.edu (Ron Guilmette) (01/14/90)
In article <18157@umn-cs.CS.UMN.EDU> mike@umn-cs.cs.umn.edu (Mike Haertel) writes: > >Just to be pedantic, this is not strictly true. It is OK to >declare a pointer to an incomplete type, so this works *here*. >But consider the following counterexample: > >/*1*/ struct b { int x; }; >/*2*/ >/*3*/ void foo() { >/*4*/ struct b; >/*5*/ struct a { struct b *b; } *a; >/*6*/ struct b { struct a *a; } *b; >/*7*/ >/*8*/ b = a->b; >/*9*/ } > >In this case, line 4 is necessary to make "struct b" an incomplete type. >Otherwise, the "struct b *b" in line 5 would refer to the "struct b" >declared on line 1... Buzzzzzzzt! Sorry, no! But thanks for playing our game. Just for being such a good sport you win a free copy of our home game, complete with the new 2.0 Reference Manual. :-) :-) :-) // rfg
rfg@ics.uci.edu (Ron Guilmette) (01/15/90)
In article <25AFE9B7.14113@paris.ics.uci.edu> rfg@ics.uci.edu (Ron Guilmette) writes: >In article <18157@umn-cs.CS.UMN.EDU> mike@umn-cs.cs.umn.edu (Mike Haertel) writes: >> >>Just to be pedantic, this is not strictly true. It is OK to >>declare a pointer to an incomplete type, so this works *here*. >>But consider the following counterexample: >> >>/*1*/ struct b { int x; }; >>/*2*/ >>/*3*/ void foo() { >>/*4*/ struct b; >>/*5*/ struct a { struct b *b; } *a; >>/*6*/ struct b { struct a *a; } *b; >>/*7*/ >>/*8*/ b = a->b; >>/*9*/ } >> >>In this case, line 4 is necessary to make "struct b" an incomplete type. >>Otherwise, the "struct b *b" in line 5 would refer to the "struct b" >>declared on line 1... > >Buzzzzzzzt! Sorry, no! But thanks for playing our game. Just for being >such a good sport you win a free copy of our home game, complete with the >new 2.0 Reference Manual. OK, before you folks send me 8 zillion letters telling me what a dumbs*** I am for having posted this, let me just say that I know already! Section 9.1 of the new 2.0 manual does indeed require the declaration of struct b in the example above. I guess that the booby prize (the 2.0 Ref. Man.) goes to me instead, together with a set of spectacles to read it with! > >:-) :-) :-) Well, at least I had to good sense to put in smiles so that nobody would think that I was being hostile. (No... not hostile... just stupid. I trust that stupidity is a forgivable sin in this particular newsgroup.) // rfg
pcg@rupert.cs.aber.ac.uk (Piercarlo Grandi) (01/15/90)
In article <25B0E7F7.6002@paris.ics.uci.edu> rfg@ics.uci.edu (Ron Guilmette) writes: In article <25AFE9B7.14113@paris.ics.uci.edu> rfg@ics.uci.edu (Ron Guilmette) writes: >In article <18157@umn-cs.CS.UMN.EDU> mike@umn-cs.cs.umn.edu (Mike Haertel) writes: >> >>Just to be pedantic, this is not strictly true. It is OK to >>declare a pointer to an incomplete type, so this works *here*. >>But consider the following counterexample: >> >>/*1*/ struct b { int x; }; >>/*2*/ >>/*3*/ void foo() { >>/*4*/ struct b; >>/*5*/ struct a { struct b *b; } *a; >>/*6*/ struct b { struct a *a; } *b; >>/*7*/ >>/*8*/ b = a->b; >>/*9*/ } >> >>In this case, line 4 is necessary to make "struct b" an incomplete type. >>Otherwise, the "struct b *b" in line 5 would refer to the "struct b" >>declared on line 1... Well, not in C; in C line 4 is an empty declaration and the type used is the one defined on line 1 (see K&R version 1, "The ice is admittedly thin here" footnote). In C++ 2.0 line 4 does declare a new type, but only because of special casing: >Buzzzzzzzt! Sorry, no! But thanks for playing our game. Just for being >such a good sport you win a free copy of our home game, complete with the >new 2.0 Reference Manual. OK, before you folks send me 8 zillion letters telling me what a dumbs*** I am for having posted this, let me just say that I know already! The special case is this: in C++ a struct or class implies an automatic typedef; a typedef in theory does not declare a new type, but only names an existing one; so line 4 should just define a new typedef called 'b' for the type defined in line 1. This means that it is virtually impossible then to define mutually recursive types in an inner scope if *both* of them have the same name as one in an outer scope. It would also make for great hidden bugs. Try the following C program: typedef short t; static struct a {struct b *b; long l[1];} a; main() { { auto t t1; typedef double t; auto t t2; printf(" size t1 %u, size t2 %u\n",sizeof t1, sizeof t2); } { struct a; struct c {struct a *a; long l[2];} c; struct a {struct c *c; long l[3];} a; printf(" size a %u, size c %u, size c.a %u\n", sizeof a, sizeof c, sizeof *(c.a)); } return 0; } This should print, on your typical 32 bit byte oriented machine, something like 8 and 2 on the first line and 16, 12, 8 on the second line. NOTE: it will fail to compile on some ancient C compilers that erroneously handle a typedef like a #define, e.g. Sun's. Actually the problem is even more subtle, and has to do with the fact that certain classes of identifiers in C/C++ have global scope even if defined in an inner scope; struct/union/class names used to be such, if memory serves me right, and one could not redefine a name for one in an inner scope. C++, and C++ 2.0, complicates scope rules even further. I am not suprised then that in the new C++ 2.0 empty declarations have been special cased to imply the definition of a NEW type: Section 9.1 of the new 2.0 manual does indeed require the declaration of struct b in the example above. ... but this adds a new wart. Well, happy hacking! :-(. -- Piercarlo "Peter" Grandi | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk Dept of CS, UCW Aberystwyth | UUCP: ...!mcvax!ukc!aber-cs!pcg Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk
pcg@aber-cs.UUCP (Piercarlo Grandi) (01/17/90)
I wrote: typedef short t; static struct a {struct b *b; long l[1];} a; main() { { auto t t1; typedef double t; auto t t2; printf(" size t1 %u, size t2 %u\n",sizeof t1, sizeof t2); } { struct a; struct c {struct a *a; long l[2];} c; struct a {struct c *c; long l[3];} a; printf(" size a %u, size c %u, size c.a %u\n", sizeof a, sizeof c, sizeof *(c.a)); } return 0; } This should print, on your typical 32 bit byte oriented machine, something like 8 and 2 on the first line and 16, 12, 8 on the second line. NOTE: it will fail to compile on some ancient C compilers that erroneously handle a typedef like a #define, e.g. Sun's. [ .... ] By the way, try it taking out the line 'struct a;'. It has pointed out to me (a few pieces of Hate Mail :->) that in Ansi C (which I love a lot :->) now 'struct b' *is* a forward declaration, just like in C++. Special casing is still strong; this means that On the other hand, of the (few: G++, Sun, Gould, Mips) compilers that I have tried, *none* could handle correctly the above code save for G++; they all chickened out at the redefinition of 't', save for one that allows for type 'short double' apparently... G++, meritoriously, handled the above monstrosity with flair, even issuing the most appropriate warnings, and printing 16, 2, 16 on the second line. What's amusing is that the presence of the 'struct a;' line does not seem to affect it. I think its absence should: as far as I know, the only (regrettable) exception to the one-pass-rule in C++ is class members, where in a member you can refer to members declared later. -- Piercarlo "Peter" Grandi | ARPA: pcg%cs.aber.ac.uk@nsfnet-relay.ac.uk Dept of CS, UCW Aberystwyth | UUCP: ...!mcvax!ukc!aber-cs!pcg Penglais, Aberystwyth SY23 3BZ, UK | INET: pcg@cs.aber.ac.uk