[comp.lang.c] Help, page 197 K&R !!!

atula@cs.keele.ac.uk (Atula Herath) (06/30/89)

Could somebody please explain me follwing para, from the K&R.

Page 197, para 7 :

"Two structures may share a common initial sequence of members; that is,
the same member may appear in two different structures if it has same type
in both and if all previous members are the same in both. (Actually, the
compiler checks that a name in two different structures has the same type
and offset in both, but if preceding members differ the construction is
nonportable.) "

What does that mean ?

I was unable to clarify that, even after doing few couple of examples. Is
this a way of sharing variables ?.


Thanks in advance

Athula


"
-- 
[5;5m
										[0;0m

pc@cs.keele.ac.uk (Phil Cornes) (06/30/89)

From article <646@kl-cs.UUCP>, by atula@cs.keele.ac.uk (Atula Herath):
> Could somebody please explain me follwing para, from the K&R.
> 
> Page 197, para 7 :
> 
> What does that mean ?
> 

This is a paragraph in the 1st edition K&R. The paragraph details a restriction
on the choice of member names in structures and unions. The restriction itself
is fairly easy to understand, though most C compilers now available don't 
enforce it so it is not as easy to demonstrate.

K&R C says that member names in structures and unions must (in general) be
unique. This is because in early implementations the names of structure and
union members were not associated with their parent names, but were only stored
as a type and offset from the start of the parent. Attempting to use the same
member name in two places would therefore usually involve trying to assign two
different type and offset values to that name (clearly not allowed). Page 197
para 7 however, goes on to say that if using the name in two places would lead
to the same type and offset values being allocated for both then this is okay.

Most current K&R compilers and ANSI compilers don't have this problem because
they associate parent names with member names, thus automatically making unique
names out of each member name so that the same member name may appear in more
than one place (though obviously not twice within the same struct or union).

---

Phil Cornes
-----------*
cdtpc@uk.ac.stafpol.cr83

---

cline@sun.soe.clarkson.edu (Marshall Cline) (07/01/89)

In article <648@kl-cs.UUCP> pc@cs.keele.ac.uk (Phil Cornes) writes:

>From article <646@kl-cs.UUCP>, by atula@cs.keele.ac.uk (Atula Herath):
>>Could somebody please explain me follwing para, from the K&R.
>>Page 197, para 7: [.....]
>>What does that mean ?

>.....
>K&R C says that member names in structures and unions must (in general) be
>unique. This is because in early implementations the names of structure and
>union members were not associated with their parent names but were only stored
>as a type and offset from the start of the parent. Attempting to use the same
>member name in two places would therefore usually involve trying to assign two
>different type and offset values to that name (clearly not allowed)....


Another reason: K&R C (and ANSI[??] -- I don't know) allow[ed|s] a pointer
to be implicitly cast to a pointer-to-struct if the appropriate "->field"
was given.  Something like the following:

	typedef struct {int squiggle, wiggle;} worm_t;
	main()
	{	char	*p;
		...
		p->squiggle = 3;  /* implicit cast of "p" to "(worm_t *)p" */
		...
	}

I personally have never used this, as it seems left over from the days when
typedef's were discouraged (what some call "Classic C").  Now that type
definitions are usually fairly short, a cast is easy.  Naturally the most
important reason of all is that the "new" way makes the code more readable.
Lastly, K&R appendix A listed it obscurely, so I was never sure how long it
would last...

Anyway, that's probably another reason early fields in a struct had to have
the same type/offset and that all the field names in all the structs had to
be unique.

Marshall
--
	________________________________________________________________
	Marshall P. Cline	ARPA:	cline@sun.soe.clarkson.edu
	ECE Department		UseNet:	uunet!sun.soe.clarkson.edu!cline
	Clarkson University	BitNet:	BH0W@CLUTX
	Potsdam, NY  13676	AT&T:	315-268-6591

chris@mimsy.UUCP (Chris Torek) (07/02/89)

>>In article <646@kl-cs.UUCP> atula@cs.keele.ac.uk (Atula Herath) asks:
>>>Could somebody please explain me [paragraph 7, p.197 of K&R-1]

>In article <648@kl-cs.UUCP> pc@cs.keele.ac.uk (Phil Cornes) writes:
>>K&R C says that member names in structures and unions must (in general) be
>>unique. This is because in early implementations the names of structure and
>>union members were not associated with their parent names but were only stored
>>as a type and offset from the start of the parent.

This is correct.  These early implementations had all but vanished by
the time K&R 1st ed. started selling, and the note in the appendix was
rather more harm than help.

In article <CLINE.89Jul1102015@sun.soe.clarkson.edu>
cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>Another reason: K&R C (and ANSI[??] -- I don't know) allow[ed|s] a pointer
>to be implicitly cast to a pointer-to-struct if the appropriate "->field"
>was given.

This is no longer true.  PCC accepted the `old' construct but with a
warning (or a fatal error if the member name was not unique).  Some
PCCs no longer accept these ever.

As I explained not long ago in this very same newsgroup, once upon a
time C had no unions.  Instead of

	struct symbol {
		char	*sym_name;
		int	sym_type;	/* one of SYM_INT or SYM_FLOAT */
		union {
			int su_int;
			float su_float;
		} sym_un;
	};

		...
		struct symbol *p = ...;
		if (p->sym_type == SYM_INT)
			ival = p->sym_un.su_int;
		else
			fval = p->sym_un.su_float;
		...

one wrote

	struct symbol {
		char	*sym_name;
		int	sym_type;	/* SYM_INT */
		int	sym_int;
	};
	struct fsymbol {
		char	*sym_name;
		int	sym_type;	/* SYM_FLOAT */
		float	sym_float;
	};

		...
		struct symbol *p = ...;
		if (p->sym_type == SYM_INT)
			ival = p->sym_int;
		else
			fval = p->sym_float;
		...

>Something like the following:
>
>	typedef struct {int squiggle, wiggle;} worm_t;
>	main()
>	{	char	*p;
>		...
>		p->squiggle = 3;  /* implicit cast of "p" to "(worm_t *)p" */
>		...
>	}

This was allowed in the same pre-union days, but only as a side effect
of never looking at both the left and right sides of the `->' operator
at the same time.  The compiler treated `->' as

	fetch offset via rhs value
	add offset to lhs value
	load word (size determined by rhs type) from pointer result

and, since all pointers were the same size as an int---after all, the
language only ran on PDP-11s, not IBM PCs with mixed models or the
like---the only requirement was that the lhs not be floating point.
(`long' did not exist then.)  One could and did legitimately write
such things as

	0777440->csr |= RK_GO;	/* start the disk */

It did not take long for this approach to be discarded.  (But, surprise
surprise, it still works in the 4BSD compiler.  Of course, you have to
use 0x20100000+0777440 on a VAX-11/780.)

>I personally have never used this, as it seems left over from the days when
>typedef's were discouraged (what some call "Classic C").

It is left over from days much further agone.  Like Classic Coke,
Classic C had a number of formulations.  This little bit of
type-ignorance is akin to the cocaine in Coca-Cola, not the sugar.
(Coke dropped the cocaine content in the early 1900s, and switched from
`cane sugar' to `cane sugar or corn syrup, whichever is cheaper' in the
late 1900s.  But they only changed the *amount* of sugar recently, when
New Coke appeared.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/02/89)

In article <646@kl-cs.UUCP> atula@cs.keele.ac.uk (Atula Herath) writes:
>"Two structures may share a common initial sequence of members; ..."
>What does that mean ?

This is explained in A8.3 in the second edition of K&R, which reflects
the current state of C structure member name constraints.  According
to K&R 1st ed., structure member names for all structure types together
constituted one big name space; however, that's not how C is currently
defined -- now each structure type has a private name space for its
members, so the same member name may be freely used in a variety of
declarations of different structures.  The cited quotation is simply
not relevant under the current member name space rules.

K&R 1st ed.:
	struct foo {
		int a;
		float b;
	};
	struct bar {
		int a;		/* legal */
		char c;
	};
	struct baz {
		double a;	/* illegal */
		int d;
	};
	struct bam {
		float e;
		int a;		/* illegal */
	};

All the above are legal according to modern rules.

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/02/89)

In article <CLINE.89Jul1102015@sun.soe.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>	{	char	*p;
>		p->squiggle = 3;  /* implicit cast of "p" to "(worm_t *)p" */

I don't think that was ever allowed; some older C compilers did permit
pointers to one structure type to be used to access members of another
structure type, but that's as weird as I recall it getting.

pat@mirror.TMC.COM (07/03/89)

This is one of the C paragraph responses:

>/* Written  3:25 am  Jul  2, 1989 by gwyn@smoke.UUCP in mirror:comp.lang.c */
>In article <CLINE.89Jul1102015@sun.soe.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>>	{	char	*p;
>>		p->squiggle = 3;  /* implicit cast of "p" to "(worm_t *)p" */
>
>I don't think that was ever allowed; some older C compilers did permit
>pointers to one structure type to be used to access members of another
>structure type, but that's as weird as I recall it getting.
>/* End of text from mirror:comp.lang.c */

I agree that this is weard and it is bad programming practice, but is
it possible that if the programmer knew what he was doing should it be
okay for him to do it?  

nw@palladium.UUCP (Neil Webber) (07/03/89)

In article <10481@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>In article <CLINE.89Jul1102015@sun.soe.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>>	{	char	*p;
>>		p->squiggle = 3;  /* implicit cast of "p" to "(worm_t *)p" */
>
>I don't think that was ever allowed; some older C compilers did permit
>pointers to one structure type to be used to access members of another
>structure type, but that's as weird as I recall it getting.

It looks to me like it got weirder than that, although I have to
confess I have no direct experience with the older systems.

Consider this gem from adb:

	double		fw;	/* Bourne-isms removed for readability */
	struct{
		long int sa;
		short	sb,sc;
	};

		.
		.
		.

	fw.sb=get(inkdot(4),itype);
	fw.sc=get(inkdot(6),itype);

Yes, that's right -- "fw" is type "double" and it is being used
as a structure for bit-twiddling purposes.

-- 
Neil Webber / Epoch Systems, Marlboro MA / (508) 481-3717
        {harvard!cfisun, linus!alliant}!palladium!nw

rec@elf115.uu.net (Roger Critchlow) (07/06/89)

In article <236100020@mirror>, pat@mirror.TMC.COM writes:
>>/* Written  3:25 am  Jul  2, 1989 by gwyn@smoke.UUCP in mirror:comp.lang.c */
>>In article <CLINE.89Jul1102015@sun.soe.clarkson.edu>
>>cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>>>	{	char	*p;
>>>		p->squiggle = 3;  /* implicit cast of "p" to "(worm_t *)p" */
>>
>>I don't think that was ever allowed; some older C compilers did permit
>>pointers to one structure type to be used to access members of another
>>structure type, but that's as weird as I recall it getting.
>
>I agree that this is weard and it is bad programming practice, but is
>it possible that if the programmer knew what he was doing should it be
>okay for him to do it?  

It got weirder still.  So long as the structure member had a unique
offset, the member could be referenced from any pointer or from any
lvalue.  Thus you could do this:

  is_an_int_value(l) long l;
  {
    struct halves { int high, low; };
  
    return (l.high == -1 || l.high == 0);
  }

If the programmer knew what he was doing, it should be okay?  But how
do _you_ figure out whether the programmer knew what he was doing when
you find his code after he's gone?  If the programmer knows what he's
doing, then he'll write the necessary casts, and get the word order
right on the first or second try.  :-)

-- rec@elf115.uu.net --

gwyn@smoke.BRL.MIL (Doug Gwyn) (07/06/89)

In article <236100020@mirror> pat@mirror.TMC.COM writes:
>I agree that this is weard and it is bad programming practice, but is
>it possible that if the programmer knew what he was doing should it be
>okay for him to do it?  

In modern C, it is still possible to pun pointers like that,
but a cast or a union must be used.  That documents quite
clearly that the programmer really did intend the ugly construct.
The old way, an inadvertent error would be silently accepted and
cause mysterious, hard to diagnose bugs.

ross@contact.uucp (Ross Ridge) (07/09/89)

In article <236100020@mirror> pat@mirror.TMC.COM writes:
>
>This is one of the C paragraph responses:
>
>>/* Written  3:25 am  Jul  2, 1989 by gwyn@smoke.UUCP in mirror:comp.lang.c */
>>In article <CLINE.89Jul1102015@sun.soe.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>>>	{	char	*p;
>>>		p->squiggle = 3;  /* implicit cast of "p" to "(worm_t *)p" */
>>
>>I don't think that was ever allowed; some older C compilers did permit
>>pointers to one structure type to be used to access members of another
>>structure type, but that's as weird as I recall it getting.
>>/* End of text from mirror:comp.lang.c */

Well check out section 14.1 of K&R. It says that "any lvalue is allowed
before . [dot operator]", and "Also, the experession before a -> is
required only to be a pointer or an integer." Of course it also says,
"Such constructs are non-portable."

>I agree that this is weard and it is bad programming practice, but is
>it possible that if the programmer knew what he was doing should it be
>okay for him to do it?  

Well for one thing C compiliers that will accept this kind of nonsense
aren't around much any more, and since the new ANSI standard certainly isn't
going to allow it either, I can't see many programmers being able to use this
"feature". But arguably any programmer that would use this practice probably
doesn't know what he's doing anyways.

Of course you can still do all the above with today's compilers (ANSI or
otherwise) you just have to use casts. (it's stil just as implimentation
dependent as before however)

char *p;
((struct vic *) 0xd000)->bkg = ((struct colours *) p)->black;

To do something like "int i; i.bkg = 2;" you probably have to do something
like "((struct vic *) &i)->bkg = 2".

The C language comes with no saftey belts, and that's one of it's great
features, but I feel the secret to good C programming is to avoid the
temptation to take too much advantage of it's flexibility.

							Ross Ridge
-- 
 l/   Hi! I'm a freem! ! Ross Ridge -- The Dreaded C64 User   ! ross@contact
[oo]                   !                                      ! ross@ziebmef
-()-                   ! A glass of root-beer has half the    ! rridge@watmath
 db                    ! alcohol as a loaf of bread.          ! ross@watcsc

scs@adam.pika.mit.edu (Steve Summit) (08/08/89)

In article <648@kl-cs.UUCP> pc@cs.keele.ac.uk (Phil Cornes) writes:
>K&R C says that member names in structures and unions must (in general) be
>unique. This is because in early implementations the names of structure and
>union members were not associated with their parent names but were only stored
>as a type and offset from the start of the parent.

In article <18350@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>This is correct.  These early implementations had all but vanished by
>the time K&R 1st ed. started selling...

Not really; the pdp11's I and everyone I knew were learning C and
Unix on all had the single struct member namespace.  As far as I
know, the pdp11 compilers still do (unless someone has gotten
energetic in 2.10bsd or the like).

In article <10481@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>In article <CLINE.89Jul1102015@sun.soe.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>>	{	char	*p;
>>		p->squiggle = 3;  /* implicit cast of "p" to "(worm_t *)p" */
>
>I don't think that was ever allowed; some older C compilers did permit
>pointers to one structure type to be used to access members of another
>structure type, but that's as weird as I recall it getting.

Doug probably hasn't had the pleasure of using a pdp11 in a
while.  Chris is correct when he describes

>[the] side effect[s]
>of never looking at both the left and right sides of the `->' operator
>at the same time.  The compiler treated `->' as
>
>	fetch offset via rhs value
>	add offset to lhs value
>	load word (size determined by rhs type) from pointer result
>
>and, since all pointers were the same size as an int---after all, the
>language only ran on PDP-11s, not IBM PCs with mixed models or the
>like---the only requirement was that the lhs not be floating point.
>(`long' did not exist then.)

I just booted up my pdp11 (whose compiler does have long ints, by
the way) and typed in the following odious little program:

	struct {int i; double d;} x;
	
	main()
	{
	int i; char *p;
	i = p = &x;
	i->i = 2; p->d = 3.4;
	printf("%d %g\n", x.i, x.d);
	}

It compiles and runs, without warnings or errors, printing "2 3.4",
as expected.  Interestingly enough, pcc (4.2 or so bsd) also
compiles it, with four warnings, but it still runs.  A lot of old
code had this cavalier attitude towards structure pointers, so
much so that pcc goes to rather heroic backwards-compatible
efforts to support it.

It has been asserted that a declaration like

	struct (int i; double d;};

is patently useless (ANSI C may even disallow it) yet one day
while browsing through the infamous "Lions book" I came across
several such structure "declarations" in the old V6 source code,
along with one of John Lions' inimitable charitable comments
explaining what "the programmer" was trying to do/getting away
with.  It didn't matter that neither a structure tag nor
variables of that type were declared, as long as the member types
and offsets got entered in the structure member symbol table,
since char pointers, int pointers, and ints (often simply
defaulted as formal parameters) were all used fairly
interchangeably back then.

                                            Steve Summit
                                            scs@adam.pika.mit.edu

henry@utzoo.uucp (Henry Spencer) (08/14/89)

In article <12397@bloom-beacon.MIT.EDU> scs@adam.pika.mit.edu (Steve Summit) writes:
>... the pdp11's I and everyone I knew were learning C and
>Unix on all had the single struct member namespace.  As far as I
>know, the pdp11 compilers still do (unless someone has gotten
>energetic in 2.10bsd or the like).

The best pdp11 C compilers were the ones from System V, back when AT&T was
still releasing a pdp11 System V.  (Actually it was more like System IV,
but that's another story...)  They did fix the struct-member namespace.
-- 
V7 /bin/mail source: 554 lines.|     Henry Spencer at U of Toronto Zoology
1989 X.400 specs: 2200+ pages. | uunet!attcan!utzoo!henry henry@zoo.toronto.edu