Xref: utzoo comp.unix.questions:8680 comp.lang.c:11791
Path: utzoo!utgpu!water!watmath!clyde!bellcore!rutgers!gatech!ncar!oddjob!mimsy!chris
From: chris@mimsy.UUCP (Chris Torek)
Newsgroups: comp.unix.questions,comp.lang.c
Subject: Re: why p->member ?
Summary: History, taste
Message-ID: <12937@mimsy.UUCP>
Date: 10 Aug 88 06:14:46 GMT
References: <16734@adm.ARPA> <474@sp7040.UUCP>
Followup-To: comp.lang.c
Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742
Lines: 83

[I am moving this discussion to comp.lang.c]

>In article <16734@adm.ARPA> stanonik@nprdc.arpa (Ron Stanonik) writes:
>>While explaining pointers to structures someone asked why
>>the -> operator was needed; ie, why couldn't the members be
>>referenced as p.member.

In article <474@sp7040.UUCP> jsp@sp7040.UUCP (John Peters) writes:
>It needs to be referenced as a pointer.

True; but to restate the question, `why does not p.member follow
the pointer automagically?'

>*p.member ... is the same as p->member

(Someone else---ark@alice?---already corrected this, but here it is
again:)  No, they are not the same.  In particular, * has lower
precedence than ., but -> has higher precedence than .:

	*p.member	equiv		*(p.member)
	p->member	equiv		(*p).member
	*p->member	equiv		*(p->member)
			etc.

Back to the original question, `why can't you use p.member?'  The
answer is `because it is defined that way'---there is (now) no
particular reason for the definition, and in some other languages
(Mesa) pointer.member is perfectly legal and automagically follows the
pointer before selecting the member.  Some people dislike this magic,
although it is inherently safe (i.e., as long as the compiler is
working, it will never do the wrong thing, since not following the
pointer is always wrong).  That is a matter of style, about which I
shall follow the Roman expression and disputandem not.

Was there ever a reason?  Yes.  Before C became strongly typed,
the `. member' operation simply computed the address of the
object on the left hand side of the `.', looked up the `member'
name in a single global table[*], and added the offset for `member'
to the address of the LHS, then grabbed the object residing
in that location.  Hence you could write such nonsense as

	float f;
	struct {
		int sign1_exp8_mant7;	/* ... bits each */
	};
	f = 123.45;
	printf("exponent = %d\n", (f.sign1_exp8_mant7 >> 7) & 0377);

Similarly, if you had a pointer and wanted the low byte, you could
write

	/* remember, pdp-11 is little-endian */
	struct foo { char lowbyte; char highbyte; } *p;
	lb = p.lowbyte;

and the compiler would cheerfully extract the low byte of the
pointer itself.  If you wanted the low byte of the object to
which p pointed, you had to write (*p).lowbyte or p->lowbyte.
-----
[*] Back then all structures shared the member namespaces.
    This is, at least in part, the root of the format
    `struct garbleblotzer { int gb_thisfield, gb_thatfield; }'
    where an abbreviated version of the name (`gb') appears in
    front of every member.  This served to distinguish between
    members of different structures that would otherwise have
    had the same name.  With the long-since introduction of
    separate name spaces for each structure, this is no longer
    necessary, but I happen to like this format anyway, since
    the member name helps the reader remember the structure name.
-----

Incidentally, the floating point `nonsense' above survives to
this day in the 4.3BSD VAX version of `adb'.  In 4.3BSD-tahoe it
has been `legitimised' as

	(*(struct bad_programming *)&f).selector

which still shows its history.  I recently replaced the whole mess
with a union (`in which', he says, watching the wheel whirl, `each
member is at offset zero . . .').
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris